02d96ad08af3b6488c54ec039372071a1f840bcc
[openwrt/staging/linusw.git] /
1 Content-Type: text/plain; charset="utf-8"
2 MIME-Version: 1.0
3 Content-Transfer-Encoding: 7bit
4 Subject: [v3, 03/13] clk: Avoid sending high rates to downstream clocks during
5 set_rate
6 From: Stephen Boyd <sboyd@codeaurora.org>
7 X-Patchwork-Id: 6063271
8 Message-Id: <1426920332-9340-4-git-send-email-sboyd@codeaurora.org>
9 To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
10 Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
11 linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
12 Viresh Kumar <viresh.kumar@linaro.org>
13 Date: Fri, 20 Mar 2015 23:45:22 -0700
14
15 If a clock is on and we call clk_set_rate() on it we may get into
16 a situation where the clock temporarily increases in rate
17 dramatically while we walk the tree and call .set_rate() ops. For
18 example, consider a case where a PLL feeds into a divider.
19 Initially the divider is set to divide by 1 and the PLL is
20 running fairly slow (100MHz). The downstream consumer of the
21 divider output can only handle rates =< 400 MHz, but the divider
22 can only choose between divisors of 1 and 4.
23
24 +-----+ +----------------+
25 | PLL |-->| div 1 or div 4 |---> consumer device
26 +-----+ +----------------+
27
28 To achieve a rate of 400MHz on the output of the divider, we
29 would have to set the rate of the PLL to 1.6 GHz and then divide
30 it by 4. The current code would set the PLL to 1.6GHz first while
31 the divider is still set to 1, thus causing the downstream
32 consumer of the clock to receive a few clock cycles of 1.6GHz
33 clock (far beyond it's maximum acceptable rate). We should be
34 changing the divider first before increasing the PLL rate to
35 avoid this problem.
36
37 Therefore, set the rate of any child clocks that are increasing
38 in rate from their current rate so that they can increase their
39 dividers if necessary. We assume that there isn't such a thing as
40 minimum rate requirements.
41
42 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
43
44 ---
45 drivers/clk/clk.c | 34 ++++++++++++++++++++++------------
46 1 file changed, 22 insertions(+), 12 deletions(-)
47
48 --- a/drivers/clk/clk.c
49 +++ b/drivers/clk/clk.c
50 @@ -1476,21 +1476,23 @@ static struct clk *clk_propagate_rate_ch
51 * walk down a subtree and set the new rates notifying the rate
52 * change on the way
53 */
54 -static void clk_change_rate(struct clk *clk)
55 +static void clk_change_rate(struct clk *clk, unsigned long best_parent_rate)
56 {
57 struct clk *child;
58 struct hlist_node *tmp;
59 unsigned long old_rate;
60 - unsigned long best_parent_rate = 0;
61 bool skip_set_rate = false;
62 struct clk *old_parent;
63
64 - old_rate = clk->rate;
65 + hlist_for_each_entry(child, &clk->children, child_node) {
66 + /* Skip children who will be reparented to another clock */
67 + if (child->new_parent && child->new_parent != clk)
68 + continue;
69 + if (child->new_rate > child->rate)
70 + clk_change_rate(child, clk->new_rate);
71 + }
72
73 - if (clk->new_parent)
74 - best_parent_rate = clk->new_parent->rate;
75 - else if (clk->parent)
76 - best_parent_rate = clk->parent->rate;
77 + old_rate = clk->rate;
78
79 if (clk->new_parent && clk->new_parent != clk->parent) {
80 old_parent = __clk_set_parent_before(clk, clk->new_parent);
81 @@ -1510,7 +1512,7 @@ static void clk_change_rate(struct clk *
82 if (!skip_set_rate && clk->ops->set_rate)
83 clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
84
85 - clk->rate = clk_recalc(clk, best_parent_rate);
86 + clk->rate = clk->new_rate;
87
88 if (clk->notifier_count && old_rate != clk->rate)
89 __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
90 @@ -1523,12 +1525,13 @@ static void clk_change_rate(struct clk *
91 /* Skip children who will be reparented to another clock */
92 if (child->new_parent && child->new_parent != clk)
93 continue;
94 - clk_change_rate(child);
95 + if (child->new_rate != child->rate)
96 + clk_change_rate(child, clk->new_rate);
97 }
98
99 /* handle the new child who might not be in clk->children yet */
100 - if (clk->new_child)
101 - clk_change_rate(clk->new_child);
102 + if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate)
103 + clk_change_rate(clk->new_child, clk->new_rate);
104 }
105
106 /**
107 @@ -1556,6 +1559,7 @@ int clk_set_rate(struct clk *clk, unsign
108 {
109 struct clk *top, *fail_clk;
110 int ret = 0;
111 + unsigned long parent_rate;
112
113 if (!clk)
114 return 0;
115 @@ -1589,8 +1593,13 @@ int clk_set_rate(struct clk *clk, unsign
116 goto out;
117 }
118
119 + if (top->parent)
120 + parent_rate = top->parent->rate;
121 + else
122 + parent_rate = 0;
123 +
124 /* change the rates */
125 - clk_change_rate(top);
126 + clk_change_rate(top, parent_rate);
127
128 out:
129 clk_prepare_unlock();