143e0795f05697e255c47901c6da87f9a40ee0fe
[openwrt/staging/dangole.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 @@ -1744,21 +1744,24 @@ static struct clk_core *clk_propagate_ra
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_core *clk)
55 +static void
56 +clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate)
57 {
58 struct clk_core *child;
59 struct hlist_node *tmp;
60 unsigned long old_rate;
61 - unsigned long best_parent_rate = 0;
62 bool skip_set_rate = false;
63 struct clk_core *old_parent;
64
65 - old_rate = clk->rate;
66 + hlist_for_each_entry(child, &clk->children, child_node) {
67 + /* Skip children who will be reparented to another clock */
68 + if (child->new_parent && child->new_parent != clk)
69 + continue;
70 + if (child->new_rate > child->rate)
71 + clk_change_rate(child, clk->new_rate);
72 + }
73
74 - if (clk->new_parent)
75 - best_parent_rate = clk->new_parent->rate;
76 - else if (clk->parent)
77 - best_parent_rate = clk->parent->rate;
78 + old_rate = clk->rate;
79
80 if (clk->new_parent && clk->new_parent != clk->parent) {
81 old_parent = __clk_set_parent_before(clk, clk->new_parent);
82 @@ -1784,7 +1787,7 @@ static void clk_change_rate(struct clk_c
83
84 trace_clk_set_rate_complete(clk, clk->new_rate);
85
86 - clk->rate = clk_recalc(clk, best_parent_rate);
87 + clk->rate = clk->new_rate;
88
89 if (clk->notifier_count && old_rate != clk->rate)
90 __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
91 @@ -1797,12 +1800,13 @@ static void clk_change_rate(struct clk_c
92 /* Skip children who will be reparented to another clock */
93 if (child->new_parent && child->new_parent != clk)
94 continue;
95 - clk_change_rate(child);
96 + if (child->new_rate != child->rate)
97 + clk_change_rate(child, clk->new_rate);
98 }
99
100 /* handle the new child who might not be in clk->children yet */
101 - if (clk->new_child)
102 - clk_change_rate(clk->new_child);
103 + if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate)
104 + clk_change_rate(clk->new_child, clk->new_rate);
105 }
106
107 static int clk_core_set_rate_nolock(struct clk_core *clk,
108 @@ -1811,6 +1815,7 @@ static int clk_core_set_rate_nolock(stru
109 struct clk_core *top, *fail_clk;
110 unsigned long rate = req_rate;
111 int ret = 0;
112 + unsigned long parent_rate;
113
114 if (!clk)
115 return 0;
116 @@ -1836,8 +1841,13 @@ static int clk_core_set_rate_nolock(stru
117 return -EBUSY;
118 }
119
120 + if (top->parent)
121 + parent_rate = top->parent->rate;
122 + else
123 + parent_rate = 0;
124 +
125 /* change the rates */
126 - clk_change_rate(top);
127 + clk_change_rate(top, parent_rate);
128
129 clk->req_rate = req_rate;
130