From patchwork Fri Dec 18 13:58:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tero Kristo X-Patchwork-Id: 58670 Delivered-To: patch@linaro.org Received: by 10.112.89.199 with SMTP id bq7csp1042008lbb; Fri, 18 Dec 2015 05:58:29 -0800 (PST) X-Received: by 10.98.19.149 with SMTP id 21mr5370204pft.18.1450447096932; Fri, 18 Dec 2015 05:58:16 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id va5si24205142pac.165.2015.12.18.05.58.16; Fri, 18 Dec 2015 05:58:16 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753274AbbLRN6L (ORCPT + 3 others); Fri, 18 Dec 2015 08:58:11 -0500 Received: from bear.ext.ti.com ([192.94.94.41]:60522 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932468AbbLRN6I (ORCPT ); Fri, 18 Dec 2015 08:58:08 -0500 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id tBIDvbpl026748; Fri, 18 Dec 2015 07:57:37 -0600 Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id tBIDvbiI007639; Fri, 18 Dec 2015 07:57:37 -0600 Received: from dlep33.itg.ti.com (157.170.170.75) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.3.224.2; Fri, 18 Dec 2015 07:57:37 -0600 Received: from localhost.localdomain (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep33.itg.ti.com (8.14.3/8.13.8) with ESMTP id tBIDvKUH021403; Fri, 18 Dec 2015 07:57:35 -0600 From: Tero Kristo To: , , , , CC: Subject: [RFC 6/9] clk: ti: add support for omap4 module clocks Date: Fri, 18 Dec 2015 15:58:58 +0200 Message-ID: <1450447141-29936-7-git-send-email-t-kristo@ti.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1450447141-29936-1-git-send-email-t-kristo@ti.com> References: <1450447141-29936-1-git-send-email-t-kristo@ti.com> MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org Previously, hwmod core has been used for controlling the hwmod level clocks. This has certain drawbacks, like being unable to share the clocks for multiple users, missing usecounting and generally being totally incompatible with common clock framework. Add support for new clock type under the TI clock driver, which will be used to convert all the existing hwmdo clocks to. This helps to get rid of the clock related hwmod data from kernel and instead parsing this from DT. Signed-off-by: Tero Kristo --- drivers/clk/ti/Makefile | 3 +- drivers/clk/ti/clkt_mod.c | 351 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/clk/ti.h | 2 + 3 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/ti/clkt_mod.c -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile index d4ac960..d1b9d41 100644 --- a/drivers/clk/ti/Makefile +++ b/drivers/clk/ti/Makefile @@ -1,7 +1,8 @@ obj-y += clk.o autoidle.o clockdomain.o clk-common = dpll.o composite.o divider.o gate.o \ fixed-factor.o mux.o apll.o \ - clkt_dpll.o clkt_iclk.o clkt_dflt.o + clkt_dpll.o clkt_iclk.o clkt_dflt.o \ + clkt_mod.o obj-$(CONFIG_SOC_AM33XX) += $(clk-common) clk-33xx.o dpll3xxx.o obj-$(CONFIG_SOC_TI81XX) += $(clk-common) fapll.o clk-814x.o clk-816x.o obj-$(CONFIG_ARCH_OMAP2) += $(clk-common) interface.o clk-2xxx.o diff --git a/drivers/clk/ti/clkt_mod.c b/drivers/clk/ti/clkt_mod.c new file mode 100644 index 0000000..186d5f7 --- /dev/null +++ b/drivers/clk/ti/clkt_mod.c @@ -0,0 +1,351 @@ +/* + * OMAP hardware module clock support + * + * Copyright (C) 2015 Texas Instruments, Inc. + * + * Tero Kristo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "clock.h" + +#undef pr_fmt +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#define OMAP4_MODULEMODE_MASK 0x3 + +#define MODULEMODE_HWCTRL 0x1 +#define MODULEMODE_SWCTRL 0x2 + +#define OMAP4_IDLEST_MASK (0x3 << 16) +#define OMAP4_IDLEST_SHIFT 16 + +#define CLKCTRL_IDLEST_FUNCTIONAL 0x0 +#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 +#define CLKCTRL_IDLEST_DISABLED 0x3 + +#define OMAP4_MAX_MODULE_READY_TIME 2000 +#define OMAP4_MAX_MODULE_DISABLE_TIME 5000 + +static u32 _omap4_idlest(u32 val) +{ + val &= OMAP4_IDLEST_MASK; + val >>= OMAP4_IDLEST_SHIFT; + + return val; +} + +static bool _omap4_is_idle(u32 val) +{ + val = _omap4_idlest(val); + + return val == CLKCTRL_IDLEST_DISABLED; +} + +static bool _omap4_is_ready(u32 val) +{ + val = _omap4_idlest(val); + + return val == CLKCTRL_IDLEST_FUNCTIONAL || + val == CLKCTRL_IDLEST_INTERFACE_IDLE; +} + +static int _omap4_hwmod_clk_enable(struct clk_hw *hw) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + u32 val; + int timeout = 0; + int ret; + + if (!clk->enable_bit) + return 0; + + if (clk->clkdm) { + ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); + if (ret) { + WARN(1, + "%s: could not enable %s's clockdomain %s: %d\n", + __func__, clk_hw_get_name(hw), + clk->clkdm_name, ret); + return ret; + } + } + + val = ti_clk_ll_ops->clk_readl(clk->enable_reg); + + val &= ~OMAP4_MODULEMODE_MASK; + val |= clk->enable_bit; + + ti_clk_ll_ops->clk_writel(val, clk->enable_reg); + + /* Wait until module is enabled */ + while (!_omap4_is_ready(val)) { + udelay(1); + timeout++; + if (timeout > OMAP4_MAX_MODULE_READY_TIME) { + pr_err("%s: failed to enable\n", clk_hw_get_name(hw)); + return -EBUSY; + } + val = ti_clk_ll_ops->clk_readl(clk->enable_reg); + } + + return 0; +} + +static void _omap4_hwmod_clk_disable(struct clk_hw *hw) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + u32 val; + int timeout = 0; + + if (!clk->enable_bit) + return; + + val = ti_clk_ll_ops->clk_readl(clk->enable_reg); + + val &= ~OMAP4_MODULEMODE_MASK; + + ti_clk_ll_ops->clk_writel(val, clk->enable_reg); + + /* Wait until module is disabled */ + while (!_omap4_is_idle(val)) { + udelay(1); + timeout++; + if (timeout > OMAP4_MAX_MODULE_DISABLE_TIME) { + pr_err("%s: failed to disable\n", clk_hw_get_name(hw)); + break; + } + val = ti_clk_ll_ops->clk_readl(clk->enable_reg); + } + + if (clk->clkdm) + ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); +} + +static int _omap4_hwmod_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + u32 val; + + val = ti_clk_ll_ops->clk_readl(clk->enable_reg); + + if (val & clk->enable_bit) + return 1; + + return 0; +} + +static const struct clk_ops omap4_module_clk_ops = { + .enable = _omap4_hwmod_clk_enable, + .disable = _omap4_hwmod_clk_disable, + .is_enabled = _omap4_hwmod_clk_is_enabled, +}; + +static void __init _of_ti_hwmod_clk_setup(struct device_node *node, + u8 modulemode) +{ + const char *parent_name; + void __iomem *reg; + u8 enable_bit; + struct clk_hw_omap *clk_hw; + struct clk_init_data init = { NULL }; + struct clk *clk; + + reg = ti_clk_get_reg_addr(node, 0); + if (IS_ERR(reg)) + return; + + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + pr_err("%s must have 1 parent\n", node->name); + return; + } + + enable_bit = modulemode; + + clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); + + clk_hw->enable_reg = reg; + clk_hw->enable_bit = enable_bit; + + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + init.ops = &omap4_module_clk_ops; + clk_hw->hw.init = &init; + init.name = node->name; + + clk = clk_register(NULL, &clk_hw->hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +static void __init of_ti_omap4_hwmod_clk_setup(struct device_node *node) +{ + _of_ti_hwmod_clk_setup(node, 0); +} +CLK_OF_DECLARE(ti_omap4_hwmod_clk, "ti,omap4-mod-clock", + of_ti_omap4_hwmod_clk_setup); + +static void __init of_ti_omap4_hwmod_hw_clk_setup(struct device_node *node) +{ + _of_ti_hwmod_clk_setup(node, MODULEMODE_HWCTRL); +} +CLK_OF_DECLARE(ti_omap4_hwmod_hw_clk, "ti,omap4-hw-mod-clock", + of_ti_omap4_hwmod_hw_clk_setup); + +static void __init of_ti_omap4_hwmod_sw_clk_setup(struct device_node *node) +{ + _of_ti_hwmod_clk_setup(node, MODULEMODE_SWCTRL); +} + +CLK_OF_DECLARE(ti_omap4_hwmod_sw_clk, "ti,omap4-sw-mod-clock", + of_ti_omap4_hwmod_sw_clk_setup); + +static u8 _omap4_mux_mod_get_parent(struct clk_hw *hw) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct clk_hw *mux_hw = clk->mux; + + __clk_hw_set_clk(mux_hw, hw); + + return ti_clk_mux_get_parent(mux_hw); +} + +static int _omap4_mux_mod_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct clk_hw *mux_hw = clk->mux; + + __clk_hw_set_clk(mux_hw, hw); + + return ti_clk_mux_set_parent(mux_hw, index); +} + +static int _omap4_mux_mod_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct clk_hw *mux_hw = clk->mux; + + __clk_hw_set_clk(mux_hw, hw); + + return __clk_mux_determine_rate(mux_hw, req); +} + +static const struct clk_ops omap4_mux_module_clk_ops = { + .enable = _omap4_hwmod_clk_enable, + .disable = _omap4_hwmod_clk_disable, + .is_enabled = _omap4_hwmod_clk_is_enabled, + .get_parent = _omap4_mux_mod_get_parent, + .set_parent = _omap4_mux_mod_set_parent, + .determine_rate = _omap4_mux_mod_determine_rate, +}; + +static void __init _of_ti_omap4_hwmod_mux_clk_setup(struct device_node *node, + u8 modulemode) +{ + struct clk_hw_omap *gate; + struct clk_mux *mux; + int num_parents; + const char **parent_names = NULL; + u32 val; + void __iomem *reg; + struct clk *clk; + struct clk_init_data init = { NULL }; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + + if (!mux || !gate) + goto err; + + gate->mux = &mux->hw; + + if (!of_property_read_u32(node, "ti,bit-shift", &val)) + mux->shift = val; + + if (of_property_read_bool(node, "ti,index-starts-at-one")) + mux->flags |= CLK_MUX_INDEX_ONE; + + num_parents = of_clk_get_parent_count(node); + + if (num_parents < 2) { + pr_err("%s: must have parents\n", node->name); + goto err; + } + + parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + goto err; + + of_clk_parent_fill(node, parent_names, num_parents); + + reg = ti_clk_get_reg_addr(node, 0); + + if (IS_ERR(reg)) + goto err; + + mux->reg = reg; + + gate->enable_bit = modulemode; + gate->enable_reg = reg; + + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = 0; + + init.ops = &omap4_mux_module_clk_ops; + gate->hw.init = &init; + init.name = node->name; + + clk = clk_register(NULL, &gate->hw); + + if (!IS_ERR(clk)) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + goto cleanup; + } +err: + kfree(gate); + kfree(mux); + +cleanup: + kfree(parent_names); +} + +static void __init of_ti_omap4_hwmod_mux_clk_setup(struct device_node *node) +{ + _of_ti_omap4_hwmod_mux_clk_setup(node, 0); +} +CLK_OF_DECLARE(ti_omap4_mux_hwmod_clk, "ti,omap4-mux-mod-clock", + of_ti_omap4_hwmod_mux_clk_setup); + +static void __init of_ti_omap4_hwmod_sw_mux_clk_setup(struct device_node *node) +{ + _of_ti_omap4_hwmod_mux_clk_setup(node, MODULEMODE_SWCTRL); +} +CLK_OF_DECLARE(ti_omap4_mux_hwmod_sw_clk, "ti,omap4-sw-mux-mod-clock", + of_ti_omap4_hwmod_sw_mux_clk_setup); + +static void __init of_ti_omap4_hwmod_hw_mux_clk_setup(struct device_node *node) +{ + _of_ti_omap4_hwmod_mux_clk_setup(node, MODULEMODE_HWCTRL); +} +CLK_OF_DECLARE(ti_omap4_mux_hwmod_hw_clk, "ti,omap4-hw-mux-mod-clock", + of_ti_omap4_hwmod_hw_mux_clk_setup); diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index ec5613a..5371dac 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -127,6 +127,7 @@ struct clk_hw_omap_ops { * @enable_bit: bitshift to write to enable/disable the clock (see @enable_reg) * @flags: see "struct clk.flags possibilities" above * @clksel_reg: for clksel clks, register va containing src/divisor select + * @mux: for module clocks, pointer to the optional mux component * @dpll_data: for DPLLs, pointer to struct dpll_data for this clock * @clkdm_name: clockdomain name that this clock is contained in * @clkdm: pointer to struct clockdomain, resolved from @clkdm_name at runtime @@ -141,6 +142,7 @@ struct clk_hw_omap { u8 enable_bit; u8 flags; void __iomem *clksel_reg; + struct clk_hw *mux; struct dpll_data *dpll_data; const char *clkdm_name; struct clockdomain *clkdm;