summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@kernel.org>2019-02-21 13:45:07 -0800
committerStephen Boyd <sboyd@kernel.org>2019-02-21 13:45:07 -0800
commit4e9a35d3d7965b90dcad72acf1933c6bdc16cb5d (patch)
tree2e472d09a09eb9bc792ddd587393b35a949e3e10
parentbfeffd155283772bbe78c6a05dec7c0128ee500c (diff)
parent12af39cad78e9cb8172793ca5d7f369eb144578b (diff)
Merge tag 'ti-clk-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/kristo/linux into clk-ti
Pull TI clock driver updates from Tero Kristo: Contains the CLK_IS_BASIC flag cleanup, getting rid of the flag completely from TI clock driver. Also contains the autoidle clock support series from Andreas Kemnade, which is needed for handling the hdq clocks properly during low power states; at least on gta04 board. * tag 'ti-clk-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/kristo/linux: ARM: OMAP2+: hwmod: disable ick autoidling when a hwmod requires that clk: ti: check clock type before doing autoidle ops clk: ti: add a usecount for autoidle clk: ti: generalize the init sequence of clk_hw_omap clocks clk: ti: remove usage of CLK_IS_BASIC clk: ti: add new API for checking if a provided clock is an OMAP clock clk: ti: move clk_hw_omap list handling under generic part of the driver
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c16
-rw-r--r--drivers/clk/ti/adpll.c2
-rw-r--r--drivers/clk/ti/apll.c4
-rw-r--r--drivers/clk/ti/autoidle.c101
-rw-r--r--drivers/clk/ti/clk.c72
-rw-r--r--drivers/clk/ti/clkctrl.c2
-rw-r--r--drivers/clk/ti/clock.h5
-rw-r--r--drivers/clk/ti/clockdomain.c2
-rw-r--r--drivers/clk/ti/divider.c2
-rw-r--r--drivers/clk/ti/dpll.c11
-rw-r--r--drivers/clk/ti/dpll3xxx.c2
-rw-r--r--drivers/clk/ti/gate.c2
-rw-r--r--drivers/clk/ti/interface.c4
-rw-r--r--drivers/clk/ti/mux.c2
-rw-r--r--include/linux/clk/ti.h1
15 files changed, 165 insertions, 63 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index b5531dd3ae9c..3a04c73ac03c 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -1002,8 +1002,10 @@ static int _enable_clocks(struct omap_hwmod *oh)
clk_enable(oh->_clk);
list_for_each_entry(os, &oh->slave_ports, node) {
- if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE))
+ if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) {
+ omap2_clk_deny_idle(os->_clk);
clk_enable(os->_clk);
+ }
}
/* The opt clocks are controlled by the device driver. */
@@ -1055,8 +1057,10 @@ static int _disable_clocks(struct omap_hwmod *oh)
clk_disable(oh->_clk);
list_for_each_entry(os, &oh->slave_ports, node) {
- if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE))
+ if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) {
clk_disable(os->_clk);
+ omap2_clk_allow_idle(os->_clk);
+ }
}
if (oh->flags & HWMOD_OPT_CLKS_NEEDED)
@@ -2436,9 +2440,13 @@ static void _setup_iclk_autoidle(struct omap_hwmod *oh)
continue;
if (os->flags & OCPIF_SWSUP_IDLE) {
- /* XXX omap_iclk_deny_idle(c); */
+ /*
+ * we might have multiple users of one iclk with
+ * different requirements, disable autoidle when
+ * the module is enabled, e.g. dss iclk
+ */
} else {
- /* XXX omap_iclk_allow_idle(c); */
+ /* we are enabling autoidle afterwards anyways */
clk_enable(os->_clk);
}
}
diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c
index 688e403333b9..0c210984765a 100644
--- a/drivers/clk/ti/adpll.c
+++ b/drivers/clk/ti/adpll.c
@@ -614,7 +614,7 @@ static int ti_adpll_init_clkout(struct ti_adpll_data *d,
init.name = child_name;
init.ops = ops;
- init.flags = CLK_IS_BASIC;
+ init.flags = 0;
co->hw.init = &init;
parent_names[0] = __clk_get_name(clk0);
parent_names[1] = __clk_get_name(clk1);
diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c
index 222f68bc3f2a..015a657d3382 100644
--- a/drivers/clk/ti/apll.c
+++ b/drivers/clk/ti/apll.c
@@ -165,7 +165,7 @@ static void __init omap_clk_register_apll(void *user,
ad->clk_bypass = __clk_get_hw(clk);
- clk = ti_clk_register(NULL, &clk_hw->hw, node->name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
kfree(clk_hw->hw.init->parent_names);
@@ -402,7 +402,7 @@ static void __init of_omap2_apll_setup(struct device_node *node)
if (ret)
goto cleanup;
- clk = clk_register(NULL, &clk_hw->hw);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
kfree(init);
diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c
index 7bb9afbe4058..1cae226759dd 100644
--- a/drivers/clk/ti/autoidle.c
+++ b/drivers/clk/ti/autoidle.c
@@ -35,7 +35,44 @@ struct clk_ti_autoidle {
#define AUTOIDLE_LOW 0x1
static LIST_HEAD(autoidle_clks);
-static LIST_HEAD(clk_hw_omap_clocks);
+
+/*
+ * we have some non-atomic read/write
+ * operations behind it, so lets
+ * take one lock for handling autoidle
+ * of all clocks
+ */
+static DEFINE_SPINLOCK(autoidle_spinlock);
+
+static int _omap2_clk_deny_idle(struct clk_hw_omap *clk)
+{
+ if (clk->ops && clk->ops->deny_idle) {
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&autoidle_spinlock, irqflags);
+ clk->autoidle_count++;
+ if (clk->autoidle_count == 1)
+ clk->ops->deny_idle(clk);
+
+ spin_unlock_irqrestore(&autoidle_spinlock, irqflags);
+ }
+ return 0;
+}
+
+static int _omap2_clk_allow_idle(struct clk_hw_omap *clk)
+{
+ if (clk->ops && clk->ops->allow_idle) {
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&autoidle_spinlock, irqflags);
+ clk->autoidle_count--;
+ if (clk->autoidle_count == 0)
+ clk->ops->allow_idle(clk);
+
+ spin_unlock_irqrestore(&autoidle_spinlock, irqflags);
+ }
+ return 0;
+}
/**
* omap2_clk_deny_idle - disable autoidle on an OMAP clock
@@ -45,12 +82,15 @@ static LIST_HEAD(clk_hw_omap_clocks);
*/
int omap2_clk_deny_idle(struct clk *clk)
{
- struct clk_hw_omap *c;
+ struct clk_hw *hw = __clk_get_hw(clk);
- c = to_clk_hw_omap(__clk_get_hw(clk));
- if (c->ops && c->ops->deny_idle)
- c->ops->deny_idle(c);
- return 0;
+ if (omap2_clk_is_hw_omap(hw)) {
+ struct clk_hw_omap *c = to_clk_hw_omap(hw);
+
+ return _omap2_clk_deny_idle(c);
+ }
+
+ return -EINVAL;
}
/**
@@ -61,12 +101,15 @@ int omap2_clk_deny_idle(struct clk *clk)
*/
int omap2_clk_allow_idle(struct clk *clk)
{
- struct clk_hw_omap *c;
+ struct clk_hw *hw = __clk_get_hw(clk);
- c = to_clk_hw_omap(__clk_get_hw(clk));
- if (c->ops && c->ops->allow_idle)
- c->ops->allow_idle(c);
- return 0;
+ if (omap2_clk_is_hw_omap(hw)) {
+ struct clk_hw_omap *c = to_clk_hw_omap(hw);
+
+ return _omap2_clk_allow_idle(c);
+ }
+
+ return -EINVAL;
}
static void _allow_autoidle(struct clk_ti_autoidle *clk)
@@ -168,26 +211,6 @@ int __init of_ti_clk_autoidle_setup(struct device_node *node)
}
/**
- * omap2_init_clk_hw_omap_clocks - initialize an OMAP clock
- * @hw: struct clk_hw * to initialize
- *
- * Add an OMAP clock @clk to the internal list of OMAP clocks. Used
- * temporarily for autoidle handling, until this support can be
- * integrated into the common clock framework code in some way. No
- * return value.
- */
-void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw)
-{
- struct clk_hw_omap *c;
-
- if (clk_hw_get_flags(hw) & CLK_IS_BASIC)
- return;
-
- c = to_clk_hw_omap(hw);
- list_add(&c->node, &clk_hw_omap_clocks);
-}
-
-/**
* omap2_clk_enable_autoidle_all - enable autoidle on all OMAP clocks that
* support it
*
@@ -198,11 +221,11 @@ void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw)
*/
int omap2_clk_enable_autoidle_all(void)
{
- struct clk_hw_omap *c;
+ int ret;
- list_for_each_entry(c, &clk_hw_omap_clocks, node)
- if (c->ops && c->ops->allow_idle)
- c->ops->allow_idle(c);
+ ret = omap2_clk_for_each(_omap2_clk_allow_idle);
+ if (ret)
+ return ret;
_clk_generic_allow_autoidle_all();
@@ -220,11 +243,11 @@ int omap2_clk_enable_autoidle_all(void)
*/
int omap2_clk_disable_autoidle_all(void)
{
- struct clk_hw_omap *c;
+ int ret;
- list_for_each_entry(c, &clk_hw_omap_clocks, node)
- if (c->ops && c->ops->deny_idle)
- c->ops->deny_idle(c);
+ ret = omap2_clk_for_each(_omap2_clk_deny_idle);
+ if (ret)
+ return ret;
_clk_generic_deny_autoidle_all();
diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c
index d0cd58534781..d1f948163d6a 100644
--- a/drivers/clk/ti/clk.c
+++ b/drivers/clk/ti/clk.c
@@ -31,6 +31,7 @@
#undef pr_fmt
#define pr_fmt(fmt) "%s: " fmt, __func__
+static LIST_HEAD(clk_hw_omap_clocks);
struct ti_clk_ll_ops *ti_clk_ll_ops;
static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS];
@@ -517,3 +518,74 @@ struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw,
return clk;
}
+
+/**
+ * ti_clk_register_omap_hw - register a clk_hw_omap to the clock framework
+ * @dev: device for this clock
+ * @hw: hardware clock handle
+ * @con: connection ID for this clock
+ *
+ * Registers a clk_hw_omap clock to the clock framewor, adds a clock alias
+ * for it, and adds the list to the available clk_hw_omap type clocks.
+ * Returns a handle to the registered clock if successful, ERR_PTR value
+ * in failure.
+ */
+struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw,
+ const char *con)
+{
+ struct clk *clk;
+ struct clk_hw_omap *oclk;
+
+ clk = ti_clk_register(dev, hw, con);
+ if (IS_ERR(clk))
+ return clk;
+
+ oclk = to_clk_hw_omap(hw);
+
+ list_add(&oclk->node, &clk_hw_omap_clocks);
+
+ return clk;
+}
+
+/**
+ * omap2_clk_for_each - call function for each registered clk_hw_omap
+ * @fn: pointer to a callback function
+ *
+ * Call @fn for each registered clk_hw_omap, passing @hw to each
+ * function. @fn must return 0 for success or any other value for
+ * failure. If @fn returns non-zero, the iteration across clocks
+ * will stop and the non-zero return value will be passed to the
+ * caller of omap2_clk_for_each().
+ */
+int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw))
+{
+ int ret;
+ struct clk_hw_omap *hw;
+
+ list_for_each_entry(hw, &clk_hw_omap_clocks, node) {
+ ret = (*fn)(hw);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * omap2_clk_is_hw_omap - check if the provided clk_hw is OMAP clock
+ * @hw: clk_hw to check if it is an omap clock or not
+ *
+ * Checks if the provided clk_hw is OMAP clock or not. Returns true if
+ * it is, false otherwise.
+ */
+bool omap2_clk_is_hw_omap(struct clk_hw *hw)
+{
+ struct clk_hw_omap *oclk;
+
+ list_for_each_entry(oclk, &clk_hw_omap_clocks, node) {
+ if (&oclk->hw == hw)
+ return true;
+ }
+
+ return false;
+}
diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c
index 40630eb950fc..bf32d996177f 100644
--- a/drivers/clk/ti/clkctrl.c
+++ b/drivers/clk/ti/clkctrl.c
@@ -276,7 +276,7 @@ _ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider,
init.parent_names = parents;
init.num_parents = num_parents;
init.ops = ops;
- init.flags = CLK_IS_BASIC;
+ init.flags = 0;
clk = ti_clk_register(NULL, clk_hw, init.name);
if (IS_ERR_OR_NULL(clk)) {
diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h
index 9f312a219510..1c0fac59d809 100644
--- a/drivers/clk/ti/clock.h
+++ b/drivers/clk/ti/clock.h
@@ -203,6 +203,8 @@ typedef void (*ti_of_clk_init_cb_t)(void *, struct device_node *);
struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw,
const char *con);
+struct clk *ti_clk_register_omap_hw(struct device *dev, struct clk_hw *hw,
+ const char *con);
int ti_clk_add_alias(struct device *dev, struct clk *clk, const char *con);
void ti_clk_add_aliases(void);
@@ -221,7 +223,6 @@ int ti_clk_retry_init(struct device_node *node, void *user,
ti_of_clk_init_cb_t func);
int ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type);
-void omap2_init_clk_hw_omap_clocks(struct clk_hw *hw);
int of_ti_clk_autoidle_setup(struct device_node *node);
void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks);
@@ -301,6 +302,8 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
unsigned long *parent_rate);
int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req);
+int omap2_clk_for_each(int (*fn)(struct clk_hw_omap *hw));
+bool omap2_clk_is_hw_omap(struct clk_hw *hw);
extern struct ti_clk_ll_ops *ti_clk_ll_ops;
diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c
index 07a805125e98..423a99b9f10c 100644
--- a/drivers/clk/ti/clockdomain.c
+++ b/drivers/clk/ti/clockdomain.c
@@ -143,7 +143,7 @@ static void __init of_ti_clockdomain_setup(struct device_node *node)
continue;
}
clk_hw = __clk_get_hw(clk);
- if (clk_hw_get_flags(clk_hw) & CLK_IS_BASIC) {
+ if (!omap2_clk_is_hw_omap(clk_hw)) {
pr_warn("can't setup clkdm for basic clk %s\n",
__clk_get_name(clk));
continue;
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index 8d77090ad94a..cb5a81963ba9 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -336,7 +336,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
init.name = name;
init.ops = &ti_clk_divider_ops;
- init.flags = flags | CLK_IS_BASIC;
+ init.flags = flags;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
index 6c3329bc116f..659dadb23279 100644
--- a/drivers/clk/ti/dpll.c
+++ b/drivers/clk/ti/dpll.c
@@ -192,10 +192,9 @@ static void __init _register_dpll(void *user,
dd->clk_bypass = __clk_get_hw(clk);
/* register the clock */
- clk = ti_clk_register(NULL, &clk_hw->hw, node->name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, node->name);
if (!IS_ERR(clk)) {
- omap2_init_clk_hw_omap_clocks(&clk_hw->hw);
of_clk_add_provider(node, of_clk_src_simple_get, clk);
kfree(clk_hw->hw.init->parent_names);
kfree(clk_hw->hw.init);
@@ -265,14 +264,12 @@ static void _register_dpll_x2(struct device_node *node,
#endif
/* register the clock */
- clk = ti_clk_register(NULL, &clk_hw->hw, name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name);
- if (IS_ERR(clk)) {
+ if (IS_ERR(clk))
kfree(clk_hw);
- } else {
- omap2_init_clk_hw_omap_clocks(&clk_hw->hw);
+ else
of_clk_add_provider(node, of_clk_src_simple_get, clk);
- }
}
#endif
diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c
index 44b6b6403753..3dde6c8c3354 100644
--- a/drivers/clk/ti/dpll3xxx.c
+++ b/drivers/clk/ti/dpll3xxx.c
@@ -731,7 +731,7 @@ static struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw)
do {
do {
hw = clk_hw_get_parent(hw);
- } while (hw && (clk_hw_get_flags(hw) & CLK_IS_BASIC));
+ } while (hw && (!omap2_clk_is_hw_omap(hw)));
if (!hw)
break;
pclk = to_clk_hw_omap(hw);
diff --git a/drivers/clk/ti/gate.c b/drivers/clk/ti/gate.c
index 1c78fff5513c..504c0e91cdc7 100644
--- a/drivers/clk/ti/gate.c
+++ b/drivers/clk/ti/gate.c
@@ -123,7 +123,7 @@ static struct clk *_register_gate(struct device *dev, const char *name,
init.flags = flags;
- clk = ti_clk_register(NULL, &clk_hw->hw, name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name);
if (IS_ERR(clk))
kfree(clk_hw);
diff --git a/drivers/clk/ti/interface.c b/drivers/clk/ti/interface.c
index 87e00c2ee957..83e34429d3b1 100644
--- a/drivers/clk/ti/interface.c
+++ b/drivers/clk/ti/interface.c
@@ -57,12 +57,10 @@ static struct clk *_register_interface(struct device *dev, const char *name,
init.num_parents = 1;
init.parent_names = &parent_name;
- clk = ti_clk_register(NULL, &clk_hw->hw, name);
+ clk = ti_clk_register_omap_hw(NULL, &clk_hw->hw, name);
if (IS_ERR(clk))
kfree(clk_hw);
- else
- omap2_init_clk_hw_omap_clocks(&clk_hw->hw);
return clk;
}
diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c
index 883bdde94d04..b7f9a4f068bf 100644
--- a/drivers/clk/ti/mux.c
+++ b/drivers/clk/ti/mux.c
@@ -143,7 +143,7 @@ static struct clk *_register_mux(struct device *dev, const char *name,
init.name = name;
init.ops = &ti_clk_mux_ops;
- init.flags = flags | CLK_IS_BASIC;
+ init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h
index eacc5df57b99..78872efc7be0 100644
--- a/include/linux/clk/ti.h
+++ b/include/linux/clk/ti.h
@@ -160,6 +160,7 @@ struct clk_hw_omap {
struct clockdomain *clkdm;
const struct clk_hw_omap_ops *ops;
u32 context;
+ int autoidle_count;
};
/*