Commit fc8726a2 authored by Dong Aisheng's avatar Dong Aisheng Committed by Stephen Boyd

clk: core: support clocks which requires parents enable (part 2)

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 adding flag CLK_OPS_PARENT_ENABLE 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 patch part 2 fixes set clock rate and set parent while its parent
is off. The most special case is for set_parent() operation which requires
all parents including both old and new one to be enabled at the same time
during the operation.

Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Shawn Guo <shawnguo@kernel.org>
Signed-off-by: default avatarDong Aisheng <aisheng.dong@nxp.com>
[sboyd@codeaurora.org: Move set_rate tracepoint after prepare_enable]
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent a4b3518d
...@@ -1172,7 +1172,9 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *core, ...@@ -1172,7 +1172,9 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *core,
struct clk_core *old_parent = core->parent; struct clk_core *old_parent = core->parent;
/* /*
* Migrate prepare state between parents and prevent race with * 1. enable parents for CLK_OPS_PARENT_ENABLE clock
*
* 2. Migrate prepare state between parents and prevent race with
* clk_enable(). * clk_enable().
* *
* If the clock is not prepared, then a race with * If the clock is not prepared, then a race with
...@@ -1188,12 +1190,17 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *core, ...@@ -1188,12 +1190,17 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *core,
* *
* See also: Comment for clk_set_parent() below. * See also: Comment for clk_set_parent() below.
*/ */
/* enable old_parent & parent if CLK_OPS_PARENT_ENABLE is set */
if (core->flags & CLK_OPS_PARENT_ENABLE) {
clk_core_prepare_enable(old_parent);
clk_core_prepare_enable(parent);
}
/* migrate prepare count if > 0 */
if (core->prepare_count) { if (core->prepare_count) {
clk_core_prepare(parent); clk_core_prepare_enable(parent);
flags = clk_enable_lock(); clk_core_enable_lock(core);
clk_core_enable(parent);
clk_core_enable(core);
clk_enable_unlock(flags);
} }
/* update the clk tree topology */ /* update the clk tree topology */
...@@ -1208,18 +1215,19 @@ static void __clk_set_parent_after(struct clk_core *core, ...@@ -1208,18 +1215,19 @@ static void __clk_set_parent_after(struct clk_core *core,
struct clk_core *parent, struct clk_core *parent,
struct clk_core *old_parent) struct clk_core *old_parent)
{ {
unsigned long flags;
/* /*
* Finish the migration of prepare state and undo the changes done * Finish the migration of prepare state and undo the changes done
* for preventing a race with clk_enable(). * for preventing a race with clk_enable().
*/ */
if (core->prepare_count) { if (core->prepare_count) {
flags = clk_enable_lock(); clk_core_disable_lock(core);
clk_core_disable(core); clk_core_disable_unprepare(old_parent);
clk_core_disable(old_parent); }
clk_enable_unlock(flags);
clk_core_unprepare(old_parent); /* re-balance ref counting if CLK_OPS_PARENT_ENABLE is set */
if (core->flags & CLK_OPS_PARENT_ENABLE) {
clk_core_disable_unprepare(parent);
clk_core_disable_unprepare(old_parent);
} }
} }
...@@ -1466,13 +1474,17 @@ static void clk_change_rate(struct clk_core *core) ...@@ -1466,13 +1474,17 @@ static void clk_change_rate(struct clk_core *core)
unsigned long best_parent_rate = 0; unsigned long best_parent_rate = 0;
bool skip_set_rate = false; bool skip_set_rate = false;
struct clk_core *old_parent; struct clk_core *old_parent;
struct clk_core *parent = NULL;
old_rate = core->rate; old_rate = core->rate;
if (core->new_parent) if (core->new_parent) {
parent = core->new_parent;
best_parent_rate = core->new_parent->rate; best_parent_rate = core->new_parent->rate;
else if (core->parent) } else if (core->parent) {
parent = core->parent;
best_parent_rate = core->parent->rate; best_parent_rate = core->parent->rate;
}
if (core->flags & CLK_SET_RATE_UNGATE) { if (core->flags & CLK_SET_RATE_UNGATE) {
unsigned long flags; unsigned long flags;
...@@ -1500,6 +1512,9 @@ static void clk_change_rate(struct clk_core *core) ...@@ -1500,6 +1512,9 @@ static void clk_change_rate(struct clk_core *core)
__clk_set_parent_after(core, core->new_parent, old_parent); __clk_set_parent_after(core, core->new_parent, old_parent);
} }
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_prepare_enable(parent);
trace_clk_set_rate(core, core->new_rate); trace_clk_set_rate(core, core->new_rate);
if (!skip_set_rate && core->ops->set_rate) if (!skip_set_rate && core->ops->set_rate)
...@@ -1518,6 +1533,9 @@ static void clk_change_rate(struct clk_core *core) ...@@ -1518,6 +1533,9 @@ static void clk_change_rate(struct clk_core *core)
clk_core_unprepare(core); clk_core_unprepare(core);
} }
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_disable_unprepare(parent);
if (core->notifier_count && old_rate != core->rate) if (core->notifier_count && old_rate != core->rate)
__clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment