123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- Content-Type: text/plain; charset="utf-8"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Subject: [v3, 03/13] clk: Avoid sending high rates to downstream clocks during
- set_rate
- From: Stephen Boyd <sboyd@codeaurora.org>
- X-Patchwork-Id: 6063271
- Message-Id: <1426920332-9340-4-git-send-email-sboyd@codeaurora.org>
- To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
- Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
- linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
- Viresh Kumar <viresh.kumar@linaro.org>
- Date: Fri, 20 Mar 2015 23:45:22 -0700
- If a clock is on and we call clk_set_rate() on it we may get into
- a situation where the clock temporarily increases in rate
- dramatically while we walk the tree and call .set_rate() ops. For
- example, consider a case where a PLL feeds into a divider.
- Initially the divider is set to divide by 1 and the PLL is
- running fairly slow (100MHz). The downstream consumer of the
- divider output can only handle rates =< 400 MHz, but the divider
- can only choose between divisors of 1 and 4.
- +-----+ +----------------+
- | PLL |-->| div 1 or div 4 |---> consumer device
- +-----+ +----------------+
- To achieve a rate of 400MHz on the output of the divider, we
- would have to set the rate of the PLL to 1.6 GHz and then divide
- it by 4. The current code would set the PLL to 1.6GHz first while
- the divider is still set to 1, thus causing the downstream
- consumer of the clock to receive a few clock cycles of 1.6GHz
- clock (far beyond it's maximum acceptable rate). We should be
- changing the divider first before increasing the PLL rate to
- avoid this problem.
- Therefore, set the rate of any child clocks that are increasing
- in rate from their current rate so that they can increase their
- dividers if necessary. We assume that there isn't such a thing as
- minimum rate requirements.
- Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
- ---
- drivers/clk/clk.c | 34 ++++++++++++++++++++++------------
- 1 file changed, 22 insertions(+), 12 deletions(-)
- --- a/drivers/clk/clk.c
- +++ b/drivers/clk/clk.c
- @@ -1476,21 +1476,23 @@ static struct clk *clk_propagate_rate_ch
- * walk down a subtree and set the new rates notifying the rate
- * change on the way
- */
- -static void clk_change_rate(struct clk *clk)
- +static void clk_change_rate(struct clk *clk, unsigned long best_parent_rate)
- {
- struct clk *child;
- struct hlist_node *tmp;
- unsigned long old_rate;
- - unsigned long best_parent_rate = 0;
- bool skip_set_rate = false;
- struct clk *old_parent;
-
- - old_rate = clk->rate;
- + hlist_for_each_entry(child, &clk->children, child_node) {
- + /* Skip children who will be reparented to another clock */
- + if (child->new_parent && child->new_parent != clk)
- + continue;
- + if (child->new_rate > child->rate)
- + clk_change_rate(child, clk->new_rate);
- + }
-
- - if (clk->new_parent)
- - best_parent_rate = clk->new_parent->rate;
- - else if (clk->parent)
- - best_parent_rate = clk->parent->rate;
- + old_rate = clk->rate;
-
- if (clk->new_parent && clk->new_parent != clk->parent) {
- old_parent = __clk_set_parent_before(clk, clk->new_parent);
- @@ -1510,7 +1512,7 @@ static void clk_change_rate(struct clk *
- if (!skip_set_rate && clk->ops->set_rate)
- clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
-
- - clk->rate = clk_recalc(clk, best_parent_rate);
- + clk->rate = clk->new_rate;
-
- if (clk->notifier_count && old_rate != clk->rate)
- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
- @@ -1523,12 +1525,13 @@ static void clk_change_rate(struct clk *
- /* Skip children who will be reparented to another clock */
- if (child->new_parent && child->new_parent != clk)
- continue;
- - clk_change_rate(child);
- + if (child->new_rate != child->rate)
- + clk_change_rate(child, clk->new_rate);
- }
-
- /* handle the new child who might not be in clk->children yet */
- - if (clk->new_child)
- - clk_change_rate(clk->new_child);
- + if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate)
- + clk_change_rate(clk->new_child, clk->new_rate);
- }
-
- /**
- @@ -1556,6 +1559,7 @@ int clk_set_rate(struct clk *clk, unsign
- {
- struct clk *top, *fail_clk;
- int ret = 0;
- + unsigned long parent_rate;
-
- if (!clk)
- return 0;
- @@ -1589,8 +1593,13 @@ int clk_set_rate(struct clk *clk, unsign
- goto out;
- }
-
- + if (top->parent)
- + parent_rate = top->parent->rate;
- + else
- + parent_rate = 0;
- +
- /* change the rates */
- - clk_change_rate(top);
- + clk_change_rate(top, parent_rate);
-
- out:
- clk_prepare_unlock();
|