summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDong Aisheng <b29396@freescale.com>2015-03-31 21:20:26 +0800
committerguoyin.chen <guoyin.chen@freescale.com>2015-05-08 17:26:03 +0800
commit0bcaab44823be5989609dc49cf0c8a6d5a22259f (patch)
treec610c4ffc2335e9f58e8ebc42641e428cadbc5ce /drivers
parent127aa6de1af8a1829dea04db11d21666addbf05d (diff)
MLK-10571-1 clk: core: add CLK_SET_PARENT_ON flags to support clocks require parent on
On Freescale i.MX7D platform, all clocks operations, including enable/disable, rate change and re-parent, requires its parent clock on. Current clock core can not support it well. This patch introduce a new flag CLK_SET_PARENT_ON to handle this special case in clock core that enable its parent clock firstly for each operation and disable it later after operation complete. The most special case is for set_parent() operation which requires both parent, old one and new one, to be enabled at the same time during the operation. Acked-by: Ranjani Vaidyanathan <Ranjani.Vaidyanathan@freescale.com> Signed-off-by: Dong Aisheng <b29396@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk.c62
1 files changed, 51 insertions, 11 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 6908d3d6a9fd..8f442f084c8b 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -491,7 +491,7 @@ static void clk_unprepare_unused_subtree(struct clk *clk)
/* caller must hold prepare_lock */
static void clk_disable_unused_subtree(struct clk *clk)
{
- struct clk *child;
+ struct clk *child, *parent = NULL;
unsigned long flags;
if (!clk)
@@ -500,6 +500,12 @@ static void clk_disable_unused_subtree(struct clk *clk)
hlist_for_each_entry(child, &clk->children, child_node)
clk_disable_unused_subtree(child);
+ if (clk->flags & CLK_SET_PARENT_ON) {
+ parent = clk_get_parent(clk);
+ WARN(!parent, "%s no parent found but has CLK_SET_PARENT_ON claimed\n", clk->name);
+ clk_prepare_enable(parent);
+ }
+
flags = clk_enable_lock();
if (clk->enable_count)
@@ -522,7 +528,8 @@ static void clk_disable_unused_subtree(struct clk *clk)
unlock_out:
clk_enable_unlock(flags);
-
+ if (clk->flags & CLK_SET_PARENT_ON)
+ clk_disable_unprepare(parent);
out:
return;
}
@@ -1232,7 +1239,7 @@ static struct clk *__clk_set_parent_before(struct clk *clk, struct clk *parent)
struct clk *old_parent = clk->parent;
/*
- * Migrate prepare state between parents and prevent race with
+ * 1. Migrate prepare state between parents and prevent race with
* clk_enable().
*
* If the clock is not prepared, then a race with
@@ -1247,11 +1254,19 @@ static struct clk *__clk_set_parent_before(struct clk *clk, struct clk *parent)
* hardware and software states.
*
* See also: Comment for clk_set_parent() below.
+ *
+ * 2. enable two parents clock for .set_parent() operation if finding
+ * flag CLK_SET_PARENT_ON
*/
- if (clk->prepare_count) {
+ if (clk->prepare_count || clk->flags & CLK_SET_PARENT_ON) {
__clk_prepare(parent);
clk_enable(parent);
- clk_enable(clk);
+ if (clk->prepare_count) {
+ clk_enable(clk);
+ } else {
+ __clk_prepare(old_parent);
+ clk_enable(old_parent);
+ }
}
/* update the clk tree topology */
@@ -1269,10 +1284,15 @@ static void __clk_set_parent_after(struct clk *clk, struct clk *parent,
* Finish the migration of prepare state and undo the changes done
* for preventing a race with clk_enable().
*/
- if (clk->prepare_count) {
- clk_disable(clk);
+ if (clk->prepare_count || clk->flags & CLK_SET_PARENT_ON) {
clk_disable(old_parent);
__clk_unprepare(old_parent);
+ if (clk->prepare_count) {
+ clk_disable(clk);
+ } else {
+ clk_disable(parent);
+ __clk_unprepare(parent);
+ }
}
/* update debugfs with new clk tree topology */
@@ -1296,11 +1316,17 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
clk_reparent(clk, old_parent);
clk_enable_unlock(flags);
- if (clk->prepare_count) {
- clk_disable(clk);
+ if (clk->prepare_count || clk->flags & CLK_SET_PARENT_ON) {
clk_disable(parent);
__clk_unprepare(parent);
+ if (clk->prepare_count) {
+ clk_disable(clk);
+ } else {
+ clk_disable(old_parent);
+ __clk_unprepare(old_parent);
+ }
}
+
return ret;
}
@@ -1493,13 +1519,17 @@ static void clk_change_rate(struct clk *clk)
unsigned long best_parent_rate = 0;
bool skip_set_rate = false;
struct clk *old_parent;
+ struct clk *parent = NULL;
old_rate = clk->rate;
- if (clk->new_parent)
+ if (clk->new_parent) {
+ parent = clk->new_parent;
best_parent_rate = clk->new_parent->rate;
- else if (clk->parent)
+ } else if (clk->parent) {
+ parent = clk->parent;
best_parent_rate = clk->parent->rate;
+ }
if (clk->new_parent && clk->new_parent != clk->parent) {
old_parent = __clk_set_parent_before(clk, clk->new_parent);
@@ -1516,6 +1546,11 @@ static void clk_change_rate(struct clk *clk)
__clk_set_parent_after(clk, clk->new_parent, old_parent);
}
+ if (clk->flags & CLK_SET_PARENT_ON && parent) {
+ __clk_prepare(parent);
+ clk_enable(parent);
+ }
+
if (!skip_set_rate && clk->ops->set_rate)
clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
@@ -1524,6 +1559,11 @@ static void clk_change_rate(struct clk *clk)
else
clk->rate = best_parent_rate;
+ if (clk->flags & CLK_SET_PARENT_ON && parent) {
+ clk_disable(parent);
+ __clk_unprepare(parent);
+ }
+
if (clk->notifier_count && old_rate != clk->rate)
__clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);