From patchwork Thu Aug 29 19:14:21 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Santosh Shilimkar X-Patchwork-Id: 2851575 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 713A79F485 for ; Thu, 29 Aug 2013 19:16:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4FA7520586 for ; Thu, 29 Aug 2013 19:16:36 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E56C0201BA for ; Thu, 29 Aug 2013 19:16:34 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VF7hH-0006yj-CF; Thu, 29 Aug 2013 19:15:59 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VF7gy-0005m8-CW; Thu, 29 Aug 2013 19:15:40 +0000 Received: from bear.ext.ti.com ([192.94.94.41]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VF7gm-0005jQ-Bx for linux-arm-kernel@lists.infradead.org; Thu, 29 Aug 2013 19:15:29 +0000 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id r7TJF2fH003492; Thu, 29 Aug 2013 14:15:02 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id r7TJF2I0008630; Thu, 29 Aug 2013 14:15:02 -0500 Received: from dflp33.itg.ti.com (10.64.6.16) by DFLE72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.2.342.3; Thu, 29 Aug 2013 14:15:01 -0500 Received: from ula0393909.am.dhcp.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp33.itg.ti.com (8.14.3/8.13.8) with ESMTP id r7TJEPsJ025499; Thu, 29 Aug 2013 14:15:01 -0500 From: Santosh Shilimkar To: Subject: [PATCH 2/3 v2] clk: keystone: Add gate control clock driver Date: Thu, 29 Aug 2013 15:14:21 -0400 Message-ID: <1377803662-3509-3-git-send-email-santosh.shilimkar@ti.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1377803662-3509-1-git-send-email-santosh.shilimkar@ti.com> References: <1377803662-3509-1-git-send-email-santosh.shilimkar@ti.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130829_151528_571547_34B6DD6B X-CRM114-Status: GOOD ( 22.62 ) X-Spam-Score: -9.4 (---------) Cc: Mark Rutland , devicetree@vger.kernel.org, Santosh Shilimkar , Mike Turquette X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-6.7 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add the driver for the clock gate control which uses PSC (Power Sleep Controller) IP on Keystone 2 based SOCs. It is responsible for enabling and disabling of the clocks for different IPs present in the SoC. Cc: Mike Turquette Cc: Mark Rutland Signed-off-by: Santosh Shilimkar --- .../devicetree/bindings/clock/keystone-gate.txt | 29 +++ drivers/clk/keystone/gate.c | 264 ++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/keystone-gate.txt create mode 100644 drivers/clk/keystone/gate.c diff --git a/Documentation/devicetree/bindings/clock/keystone-gate.txt b/Documentation/devicetree/bindings/clock/keystone-gate.txt new file mode 100644 index 0000000..c5aa187 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/keystone-gate.txt @@ -0,0 +1,29 @@ +Status: Unstable - ABI compatibility may be broken in the future + +Binding for Keystone gate control driver which uses PSC controller IP. + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "ti,keystone,psc-clock". +- #clock-cells : from common clock binding; shall be set to 0. +- clocks : parent clock phandle +- reg : psc control and domain address address space +- reg-names : psc control and domain registers +- domain-id : psc domain id needed to check the transition state register + +Optional properties: +- clock-output-names : From common clock binding to override the + default output clock name +Example: + clkusb: clkusb { + #clock-cells = <0>; + compatible = "ti,keystone,psc-clock"; + clocks = <&chipclk16>; + clock-output-names = "usb"; + reg = <0x02350008 0xb00>, <0x02350000 0x400>; + reg-names = "control", "domain"; + domain-id = <0>; + }; diff --git a/drivers/clk/keystone/gate.c b/drivers/clk/keystone/gate.c new file mode 100644 index 0000000..1f333bc --- /dev/null +++ b/drivers/clk/keystone/gate.c @@ -0,0 +1,264 @@ +/* + * Clock driver for Keystone 2 based devices + * + * Copyright (C) 2013 Texas Instruments. + * Murali Karicheri + * Santosh Shilimkar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* PSC register offsets */ +#define PTCMD 0x120 +#define PTSTAT 0x128 +#define PDSTAT 0x200 +#define PDCTL 0x300 +#define MDSTAT 0x800 +#define MDCTL 0xa00 + +/* PSC module states */ +#define PSC_STATE_SWRSTDISABLE 0 +#define PSC_STATE_SYNCRST 1 +#define PSC_STATE_DISABLE 2 +#define PSC_STATE_ENABLE 3 + +#define MDSTAT_STATE_MASK 0x3f +#define MDSTAT_MCKOUT BIT(12) +#define PDSTAT_STATE_MASK 0x1f +#define MDCTL_FORCE BIT(31) +#define MDCTL_LRESET BIT(8) +#define PDCTL_NEXT BIT(0) + +/* Maximum timeout to bail out state transition for module */ +#define STATE_TRANS_MAX_COUNT 0xffff + +static void __iomem *domain_transition_base; + +/** + * struct clk_psc_data - PSC data + * @control_base: Base address for a PSC control + * @domain_base: Base address for a PSC domain + * @domain_id: PSC domain id number + */ +struct clk_psc_data { + void __iomem *control_base; + void __iomem *domain_base; + u32 domain_id; +}; + +/** + * struct clk_psc - PSC clock structure + * @hw: clk_hw for the psc + * @psc_data: PSC driver specific data + * @lock: Spinlock used by the driver + */ +struct clk_psc { + struct clk_hw hw; + struct clk_psc_data *psc_data; + spinlock_t *lock; +}; + +static DEFINE_SPINLOCK(psc_lock); + +#define to_clk_psc(_hw) container_of(_hw, struct clk_psc, hw) + +static void psc_config(void __iomem *control_base, void __iomem *domain_base, + u32 next_state, u32 domain_id) +{ + u32 ptcmd, pdstat, pdctl, mdstat, mdctl, ptstat; + u32 count = STATE_TRANS_MAX_COUNT; + + mdctl = readl(control_base + MDCTL); + mdctl &= ~MDSTAT_STATE_MASK; + mdctl |= next_state; + /* For disable, we always put the module in local reset */ + if (next_state == PSC_STATE_DISABLE) + mdctl &= ~MDCTL_LRESET; + writel(mdctl, control_base + MDCTL); + + pdstat = readl(domain_base + PDSTAT); + if (!(pdstat & PDSTAT_STATE_MASK)) { + pdctl = readl(domain_base + PDCTL); + pdctl |= PDCTL_NEXT; + writel(pdctl, domain_base + PDCTL); + } + + ptcmd = 1 << domain_id; + writel(ptcmd, domain_transition_base + PTCMD); + do { + ptstat = readl(domain_transition_base + PTSTAT); + } while (((ptstat >> domain_id) & 1) && count--); + + count = STATE_TRANS_MAX_COUNT; + do { + mdstat = readl(control_base + MDSTAT); + } while (!((mdstat & MDSTAT_STATE_MASK) == next_state) && count--); +} + +static int keystone_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + u32 mdstat = readl(data->control_base + MDSTAT); + + return (mdstat & MDSTAT_MCKOUT) ? 1 : 0; +} + +static int keystone_clk_enable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + unsigned long flags = 0; + + if (psc->lock) + spin_lock_irqsave(psc->lock, flags); + + psc_config(data->control_base, data->domain_base, + PSC_STATE_ENABLE, data->domain_id); + + if (psc->lock) + spin_unlock_irqrestore(psc->lock, flags); + + return 0; +} + +static void keystone_clk_disable(struct clk_hw *hw) +{ + struct clk_psc *psc = to_clk_psc(hw); + struct clk_psc_data *data = psc->psc_data; + unsigned long flags = 0; + + if (psc->lock) + spin_lock_irqsave(psc->lock, flags); + + psc_config(data->control_base, data->domain_base, + PSC_STATE_DISABLE, data->domain_id); + + if (psc->lock) + spin_unlock_irqrestore(psc->lock, flags); +} + +static const struct clk_ops clk_psc_ops = { + .enable = keystone_clk_enable, + .disable = keystone_clk_disable, + .is_enabled = keystone_clk_is_enabled, +}; + +/** + * clk_register_psc - register psc clock + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @psc_data: platform data to configure this clock + * @lock: spinlock used by this clock + */ +static struct clk *clk_register_psc(struct device *dev, + const char *name, + const char *parent_name, + struct clk_psc_data *psc_data, + spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_psc *psc; + struct clk *clk; + + psc = kzalloc(sizeof(*psc), GFP_KERNEL); + if (!psc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_psc_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + psc->psc_data = psc_data; + psc->lock = lock; + psc->hw.init = &init; + + clk = clk_register(NULL, &psc->hw); + if (IS_ERR(clk)) + kfree(psc); + + return clk; +} + +/** + * of_psc_clk_init - initialize psc clock through DT + * @node: device tree node for this clock + * @lock: spinlock used by this clock + */ +static void __init of_psc_clk_init(struct device_node *node, spinlock_t *lock) +{ + const char *clk_name = node->name; + const char *parent_name; + struct clk_psc_data *data; + struct clk *clk; + int i; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + pr_err("%s: Out of memory\n", __func__); + return; + } + + i = of_property_match_string(node, "reg-names", "control"); + data->control_base = of_iomap(node, i); + if (!data->control_base) { + pr_err("%s: control ioremap failed\n", __func__); + goto out; + } + + i = of_property_match_string(node, "reg-names", "domain"); + data->domain_base = of_iomap(node, i); + if (!data->domain_base) { + pr_err("%s: domain ioremap failed\n", __func__); + iounmap(data->control_base); + goto out; + } + + of_property_read_u32(node, "domain-id", &data->domain_id); + + /* Domain transition registers at fixed address space of domain_id 0 */ + if (!domain_transition_base && !data->domain_id) + domain_transition_base = data->domain_base; + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + pr_err("%s: Parent clock not found\n", __func__); + goto out; + } + + clk = clk_register_psc(NULL, clk_name, parent_name, data, lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + return; + } + + pr_err("%s: error registering clk %s\n", __func__, node->name); +out: + kfree(data); + return; +} + +/** + * of_keystone_psc_clk_init - initialize psc clock through DT + * @node: device tree node for this clock + */ +static void __init of_keystone_psc_clk_init(struct device_node *node) +{ + of_psc_clk_init(node, &psc_lock); +} +CLK_OF_DECLARE(keystone_gate_clk, "ti,keystone,psc-clock", + of_keystone_psc_clk_init);