All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tero Kristo <t-kristo@ti.com>
To: <linux-omap@vger.kernel.org>, <linux-clk@vger.kernel.org>,
	<sboyd@codeaurora.org>, <mturquette@baylibre.com>,
	<tony@atomide.com>
Cc: <linux-arm-kernel@lists.infradead.org>
Subject: [RESEND PATCHv2 23/28] clk: ti: add support for omap4 module clocks
Date: Mon, 13 Jun 2016 22:04:57 +0300	[thread overview]
Message-ID: <1465844702-12200-24-git-send-email-t-kristo@ti.com> (raw)
In-Reply-To: <1465844702-12200-1-git-send-email-t-kristo@ti.com>

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 <t-kristo@ti.com>
---
 drivers/clk/ti/Makefile   |   3 +-
 drivers/clk/ti/clkt_mod.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk/ti.h    |   2 +
 3 files changed, 418 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/ti/clkt_mod.c

diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile
index 0deac98..15886ef 100644
--- a/drivers/clk/ti/Makefile
+++ b/drivers/clk/ti/Makefile
@@ -3,7 +3,8 @@ ifeq ($(CONFIG_ARCH_OMAP2PLUS), y)
 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..9a29eb4
--- /dev/null
+++ b/drivers/clk/ti/clkt_mod.c
@@ -0,0 +1,414 @@
+/*
+ * OMAP hardware module clock support
+ *
+ * Copyright (C) 2015 Texas Instruments, Inc.
+ *
+ * Tero Kristo <t-kristo@ti.com>
+ *
+ * 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 <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk/ti.h>
+#include <linux/delay.h>
+#include "clock.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#define NO_IDLEST			0x1
+
+#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
+
+/* These timeouts are in us */
+#define OMAP4_MAX_MODULE_READY_TIME	2000
+#define OMAP4_MAX_MODULE_DISABLE_TIME	5000
+
+static bool _early_timeout = true;
+
+union omap4_timeout {
+	u32 cycles;
+	ktime_t start;
+};
+
+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 bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout)
+{
+	if (unlikely(_early_timeout)) {
+		if (time->cycles++ < timeout) {
+			udelay(1);
+			return false;
+		}
+	} else {
+		if (!ktime_to_ns(time->start)) {
+			time->start = ktime_get();
+			return false;
+		}
+
+		if (ktime_us_delta(ktime_get(), time->start) < timeout) {
+			cpu_relax();
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int __init _omap4_disable_early_timeout(void)
+{
+	_early_timeout = false;
+
+	return 0;
+}
+arch_initcall(_omap4_disable_early_timeout);
+
+static int _omap4_hwmod_clk_enable(struct clk_hw *hw)
+{
+	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+	u32 val;
+	int ret;
+	union omap4_timeout timeout = { 0 };
+
+	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);
+
+	if (clk->flags & NO_IDLEST)
+		return 0;
+
+	/* Wait until module is enabled */
+	while (!_omap4_is_ready(ti_clk_ll_ops->clk_readl(clk->enable_reg))) {
+		if (_omap4_is_timeout(&timeout, OMAP4_MAX_MODULE_READY_TIME)) {
+			pr_err("%s: failed to enable\n", clk_hw_get_name(hw));
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static void _omap4_hwmod_clk_disable(struct clk_hw *hw)
+{
+	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+	u32 val;
+	union omap4_timeout 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);
+
+	if (clk->flags & NO_IDLEST)
+		return;
+
+	/* Wait until module is disabled */
+	while (!_omap4_is_idle(ti_clk_ll_ops->clk_readl(clk->enable_reg))) {
+		if (_omap4_is_timeout(&timeout,
+				      OMAP4_MAX_MODULE_DISABLE_TIME)) {
+			pr_err("%s: failed to disable\n", clk_hw_get_name(hw));
+			break;
+		}
+	}
+
+	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, u8 flags)
+{
+	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->flags = flags;
+
+	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, 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, 0);
+}
+
+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, 0);
+}
+
+CLK_OF_DECLARE(ti_omap4_hwmod_sw_clk, "ti,omap4-sw-mod-clock",
+	       of_ti_omap4_hwmod_sw_clk_setup);
+
+static void __init
+of_ti_omap4_hwmod_sw_no_idlest_clk_setup(struct device_node *node)
+{
+	_of_ti_hwmod_clk_setup(node, MODULEMODE_SWCTRL, NO_IDLEST);
+}
+
+CLK_OF_DECLARE(ti_omap4_hwmod_sw_no_idlest_clk,
+	       "ti,omap4-sw-no-idlest-mod-clock",
+	       of_ti_omap4_hwmod_sw_no_idlest_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;
+
+	gate->enable_bit = modulemode;
+	gate->enable_reg = reg;
+
+	reg = ti_clk_get_reg_addr(node, 1);
+
+	if (IS_ERR(reg))
+		goto err;
+
+	mux->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 0269208..adab762 100644
--- a/include/linux/clk/ti.h
+++ b/include/linux/clk/ti.h
@@ -129,6 +129,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
@@ -143,6 +144,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;
-- 
1.9.1

WARNING: multiple messages have this Message-ID (diff)
From: Tero Kristo <t-kristo@ti.com>
To: linux-omap@vger.kernel.org, linux-clk@vger.kernel.org,
	sboyd@codeaurora.org, mturquette@baylibre.com, tony@atomide.com
Cc: linux-arm-kernel@lists.infradead.org
Subject: [RESEND PATCHv2 23/28] clk: ti: add support for omap4 module clocks
Date: Mon, 13 Jun 2016 22:04:57 +0300	[thread overview]
Message-ID: <1465844702-12200-24-git-send-email-t-kristo@ti.com> (raw)
In-Reply-To: <1465844702-12200-1-git-send-email-t-kristo@ti.com>

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 <t-kristo@ti.com>
---
 drivers/clk/ti/Makefile   |   3 +-
 drivers/clk/ti/clkt_mod.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk/ti.h    |   2 +
 3 files changed, 418 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/ti/clkt_mod.c

diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile
index 0deac98..15886ef 100644
--- a/drivers/clk/ti/Makefile
+++ b/drivers/clk/ti/Makefile
@@ -3,7 +3,8 @@ ifeq ($(CONFIG_ARCH_OMAP2PLUS), y)
 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..9a29eb4
--- /dev/null
+++ b/drivers/clk/ti/clkt_mod.c
@@ -0,0 +1,414 @@
+/*
+ * OMAP hardware module clock support
+ *
+ * Copyright (C) 2015 Texas Instruments, Inc.
+ *
+ * Tero Kristo <t-kristo@ti.com>
+ *
+ * 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 <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk/ti.h>
+#include <linux/delay.h>
+#include "clock.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#define NO_IDLEST			0x1
+
+#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
+
+/* These timeouts are in us */
+#define OMAP4_MAX_MODULE_READY_TIME	2000
+#define OMAP4_MAX_MODULE_DISABLE_TIME	5000
+
+static bool _early_timeout = true;
+
+union omap4_timeout {
+	u32 cycles;
+	ktime_t start;
+};
+
+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 bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout)
+{
+	if (unlikely(_early_timeout)) {
+		if (time->cycles++ < timeout) {
+			udelay(1);
+			return false;
+		}
+	} else {
+		if (!ktime_to_ns(time->start)) {
+			time->start = ktime_get();
+			return false;
+		}
+
+		if (ktime_us_delta(ktime_get(), time->start) < timeout) {
+			cpu_relax();
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int __init _omap4_disable_early_timeout(void)
+{
+	_early_timeout = false;
+
+	return 0;
+}
+arch_initcall(_omap4_disable_early_timeout);
+
+static int _omap4_hwmod_clk_enable(struct clk_hw *hw)
+{
+	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+	u32 val;
+	int ret;
+	union omap4_timeout timeout = { 0 };
+
+	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);
+
+	if (clk->flags & NO_IDLEST)
+		return 0;
+
+	/* Wait until module is enabled */
+	while (!_omap4_is_ready(ti_clk_ll_ops->clk_readl(clk->enable_reg))) {
+		if (_omap4_is_timeout(&timeout, OMAP4_MAX_MODULE_READY_TIME)) {
+			pr_err("%s: failed to enable\n", clk_hw_get_name(hw));
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static void _omap4_hwmod_clk_disable(struct clk_hw *hw)
+{
+	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+	u32 val;
+	union omap4_timeout 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);
+
+	if (clk->flags & NO_IDLEST)
+		return;
+
+	/* Wait until module is disabled */
+	while (!_omap4_is_idle(ti_clk_ll_ops->clk_readl(clk->enable_reg))) {
+		if (_omap4_is_timeout(&timeout,
+				      OMAP4_MAX_MODULE_DISABLE_TIME)) {
+			pr_err("%s: failed to disable\n", clk_hw_get_name(hw));
+			break;
+		}
+	}
+
+	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, u8 flags)
+{
+	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->flags = flags;
+
+	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, 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, 0);
+}
+
+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, 0);
+}
+
+CLK_OF_DECLARE(ti_omap4_hwmod_sw_clk, "ti,omap4-sw-mod-clock",
+	       of_ti_omap4_hwmod_sw_clk_setup);
+
+static void __init
+of_ti_omap4_hwmod_sw_no_idlest_clk_setup(struct device_node *node)
+{
+	_of_ti_hwmod_clk_setup(node, MODULEMODE_SWCTRL, NO_IDLEST);
+}
+
+CLK_OF_DECLARE(ti_omap4_hwmod_sw_no_idlest_clk,
+	       "ti,omap4-sw-no-idlest-mod-clock",
+	       of_ti_omap4_hwmod_sw_no_idlest_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;
+
+	gate->enable_bit = modulemode;
+	gate->enable_reg = reg;
+
+	reg = ti_clk_get_reg_addr(node, 1);
+
+	if (IS_ERR(reg))
+		goto err;
+
+	mux->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 0269208..adab762 100644
--- a/include/linux/clk/ti.h
+++ b/include/linux/clk/ti.h
@@ -129,6 +129,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
@@ -143,6 +144,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;
-- 
1.9.1


WARNING: multiple messages have this Message-ID (diff)
From: t-kristo@ti.com (Tero Kristo)
To: linux-arm-kernel@lists.infradead.org
Subject: [RESEND PATCHv2 23/28] clk: ti: add support for omap4 module clocks
Date: Mon, 13 Jun 2016 22:04:57 +0300	[thread overview]
Message-ID: <1465844702-12200-24-git-send-email-t-kristo@ti.com> (raw)
In-Reply-To: <1465844702-12200-1-git-send-email-t-kristo@ti.com>

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 <t-kristo@ti.com>
---
 drivers/clk/ti/Makefile   |   3 +-
 drivers/clk/ti/clkt_mod.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk/ti.h    |   2 +
 3 files changed, 418 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/ti/clkt_mod.c

diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile
index 0deac98..15886ef 100644
--- a/drivers/clk/ti/Makefile
+++ b/drivers/clk/ti/Makefile
@@ -3,7 +3,8 @@ ifeq ($(CONFIG_ARCH_OMAP2PLUS), y)
 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..9a29eb4
--- /dev/null
+++ b/drivers/clk/ti/clkt_mod.c
@@ -0,0 +1,414 @@
+/*
+ * OMAP hardware module clock support
+ *
+ * Copyright (C) 2015 Texas Instruments, Inc.
+ *
+ * Tero Kristo <t-kristo@ti.com>
+ *
+ * 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 <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk/ti.h>
+#include <linux/delay.h>
+#include "clock.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#define NO_IDLEST			0x1
+
+#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
+
+/* These timeouts are in us */
+#define OMAP4_MAX_MODULE_READY_TIME	2000
+#define OMAP4_MAX_MODULE_DISABLE_TIME	5000
+
+static bool _early_timeout = true;
+
+union omap4_timeout {
+	u32 cycles;
+	ktime_t start;
+};
+
+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 bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout)
+{
+	if (unlikely(_early_timeout)) {
+		if (time->cycles++ < timeout) {
+			udelay(1);
+			return false;
+		}
+	} else {
+		if (!ktime_to_ns(time->start)) {
+			time->start = ktime_get();
+			return false;
+		}
+
+		if (ktime_us_delta(ktime_get(), time->start) < timeout) {
+			cpu_relax();
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int __init _omap4_disable_early_timeout(void)
+{
+	_early_timeout = false;
+
+	return 0;
+}
+arch_initcall(_omap4_disable_early_timeout);
+
+static int _omap4_hwmod_clk_enable(struct clk_hw *hw)
+{
+	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+	u32 val;
+	int ret;
+	union omap4_timeout timeout = { 0 };
+
+	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);
+
+	if (clk->flags & NO_IDLEST)
+		return 0;
+
+	/* Wait until module is enabled */
+	while (!_omap4_is_ready(ti_clk_ll_ops->clk_readl(clk->enable_reg))) {
+		if (_omap4_is_timeout(&timeout, OMAP4_MAX_MODULE_READY_TIME)) {
+			pr_err("%s: failed to enable\n", clk_hw_get_name(hw));
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static void _omap4_hwmod_clk_disable(struct clk_hw *hw)
+{
+	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+	u32 val;
+	union omap4_timeout 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);
+
+	if (clk->flags & NO_IDLEST)
+		return;
+
+	/* Wait until module is disabled */
+	while (!_omap4_is_idle(ti_clk_ll_ops->clk_readl(clk->enable_reg))) {
+		if (_omap4_is_timeout(&timeout,
+				      OMAP4_MAX_MODULE_DISABLE_TIME)) {
+			pr_err("%s: failed to disable\n", clk_hw_get_name(hw));
+			break;
+		}
+	}
+
+	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, u8 flags)
+{
+	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->flags = flags;
+
+	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, 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, 0);
+}
+
+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, 0);
+}
+
+CLK_OF_DECLARE(ti_omap4_hwmod_sw_clk, "ti,omap4-sw-mod-clock",
+	       of_ti_omap4_hwmod_sw_clk_setup);
+
+static void __init
+of_ti_omap4_hwmod_sw_no_idlest_clk_setup(struct device_node *node)
+{
+	_of_ti_hwmod_clk_setup(node, MODULEMODE_SWCTRL, NO_IDLEST);
+}
+
+CLK_OF_DECLARE(ti_omap4_hwmod_sw_no_idlest_clk,
+	       "ti,omap4-sw-no-idlest-mod-clock",
+	       of_ti_omap4_hwmod_sw_no_idlest_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;
+
+	gate->enable_bit = modulemode;
+	gate->enable_reg = reg;
+
+	reg = ti_clk_get_reg_addr(node, 1);
+
+	if (IS_ERR(reg))
+		goto err;
+
+	mux->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 0269208..adab762 100644
--- a/include/linux/clk/ti.h
+++ b/include/linux/clk/ti.h
@@ -129,6 +129,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
@@ -143,6 +144,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;
-- 
1.9.1

  parent reply	other threads:[~2016-06-13 19:04 UTC|newest]

Thread overview: 129+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-13 19:04 [RESEND PATCHv2 00/28] clk: ti: hwmod clock type support Tero Kristo
2016-06-13 19:04 ` Tero Kristo
2016-06-13 19:04 ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 01/28] clk: ti: add ti_clk_get helper API Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-28  6:47   ` Tony Lindgren
2016-06-28  6:47     ` Tony Lindgren
2016-06-29  6:17     ` Tero Kristo
2016-06-29  6:17       ` Tero Kristo
2016-06-29  6:17       ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 02/28] clk: ti: dpll: use ti_clk_get to fetch ref/bypass clocks Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 03/28] ARM: OMAP2+: omap_device: create clock alias purely from DT data Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-28  7:00   ` Tony Lindgren
2016-06-28  7:00     ` Tony Lindgren
2016-06-29  7:08     ` Tero Kristo
2016-06-29  7:08       ` Tero Kristo
2016-06-29  7:08       ` Tero Kristo
2016-06-29  7:10       ` Tero Kristo
2016-06-29  7:10         ` Tero Kristo
2016-06-29  7:10         ` Tero Kristo
2016-06-29  7:54         ` Tony Lindgren
2016-06-29  7:54           ` Tony Lindgren
2016-06-29  9:00           ` Tero Kristo
2016-06-29  9:00             ` Tero Kristo
2016-06-29  9:00             ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 04/28] ARM: OMAP2+: hwmod: use new ti_clk_get API to search for clock handles Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-28  6:57   ` Tony Lindgren
2016-06-28  6:57     ` Tony Lindgren
2016-06-29  6:16     ` Tero Kristo
2016-06-29  6:16       ` Tero Kristo
2016-06-29  6:16       ` Tero Kristo
2016-06-29  7:01       ` Tony Lindgren
2016-06-29  7:01         ` Tony Lindgren
2016-06-13 19:04 ` [RESEND PATCHv2 05/28] ARM: OMAP2+: clock: use the new ti_clk_get for fetching clocks Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 06/28] ARM: OMAP2+: hwmod: fetch main_clk based on hwmod name Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-28  7:03   ` Tony Lindgren
2016-06-28  7:03     ` Tony Lindgren
2016-06-13 19:04 ` [RESEND PATCHv2 07/28] ARM: OMAP2+: timer: change order of hwmod data handling Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-28  7:04   ` Tony Lindgren
2016-06-28  7:04     ` Tony Lindgren
2016-06-13 19:04 ` [RESEND PATCHv2 08/28] ARM: OMAP2+: clockdomain: add usecounting support to autoidle APIs Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 23:31   ` Nishanth Menon
2016-06-13 23:31     ` Nishanth Menon
2016-06-13 23:31     ` Nishanth Menon
2016-06-14  6:31     ` Tero Kristo
2016-06-14  6:31       ` Tero Kristo
2016-06-14  6:31       ` Tero Kristo
2016-06-28  7:06   ` Tony Lindgren
2016-06-28  7:06     ` Tony Lindgren
2016-06-13 19:04 ` [RESEND PATCHv2 09/28] ARM: AM33xx: fix module_wait_ready without clkctrl register Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-28  7:08   ` Tony Lindgren
2016-06-28  7:08     ` Tony Lindgren
2016-06-13 19:04 ` [RESEND PATCHv2 10/28] clk: ti: omap2: transition to usage of ti_clk_get Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 11/28] clk: ti: am33xx: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 12/28] clk: ti: omap3: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 13/28] clk: ti: am43xx: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 14/28] clk: ti: omap4: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 15/28] clk: ti: omap5: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 16/28] clk: ti: dra7: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 17/28] clk: ti: dm814x: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 18/28] clk: ti: dm816x: cleanup any unnecessary clock aliases Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 19/28] clk: ti: remove un-used definitions from public clk_hw_omap struct Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 20/28] clk: ti: mux: export mux clock APIs locally Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 21/28] clk: ti: am33xx: fix timer3/6 init time setup for module clocks Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 22/28] dt-bindings: clk: ti: Document module clock type Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` Tero Kristo [this message]
2016-06-13 19:04   ` [RESEND PATCHv2 23/28] clk: ti: add support for omap4 module clocks Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 24/28] ARM: dts: omap4: add hwmod " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04 ` [RESEND PATCHv2 25/28] ARM: dts: am33xx: " Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:04   ` Tero Kristo
2016-06-13 19:05 ` [RESEND PATCHv2 26/28] ARM: dts: am43xx: " Tero Kristo
2016-06-13 19:05   ` Tero Kristo
2016-06-13 19:05   ` Tero Kristo
2016-06-13 19:05 ` [RESEND PATCHv2 27/28] ARM: dts: omap5: " Tero Kristo
2016-06-13 19:05   ` Tero Kristo
2016-06-13 19:05   ` Tero Kristo
2016-06-13 19:05 ` [RESEND PATCHv2 28/28] ARM: dts: dra7: " Tero Kristo
2016-06-13 19:05   ` Tero Kristo
2016-06-13 19:05   ` Tero Kristo
2016-06-20 12:10 ` [RESEND PATCHv2 00/28] clk: ti: hwmod clock type support Tony Lindgren
2016-06-20 12:10   ` Tony Lindgren
2016-06-20 12:10   ` Tony Lindgren

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1465844702-12200-24-git-send-email-t-kristo@ti.com \
    --to=t-kristo@ti.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=sboyd@codeaurora.org \
    --cc=tony@atomide.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.