aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch')
-rw-r--r--target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch130
1 files changed, 130 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch b/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch
new file mode 100644
index 0000000..143e079
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch
@@ -0,0 +1,130 @@
+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
+@@ -1744,21 +1744,24 @@ static struct clk_core *clk_propagate_ra
+ * walk down a subtree and set the new rates notifying the rate
+ * change on the way
+ */
+-static void clk_change_rate(struct clk_core *clk)
++static void
++clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate)
+ {
+ struct clk_core *child;
+ struct hlist_node *tmp;
+ unsigned long old_rate;
+- unsigned long best_parent_rate = 0;
+ bool skip_set_rate = false;
+ struct clk_core *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);
+@@ -1784,7 +1787,7 @@ static void clk_change_rate(struct clk_c
+
+ trace_clk_set_rate_complete(clk, clk->new_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);
+@@ -1797,12 +1800,13 @@ static void clk_change_rate(struct clk_c
+ /* 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);
+ }
+
+ static int clk_core_set_rate_nolock(struct clk_core *clk,
+@@ -1811,6 +1815,7 @@ static int clk_core_set_rate_nolock(stru
+ struct clk_core *top, *fail_clk;
+ unsigned long rate = req_rate;
+ int ret = 0;
++ unsigned long parent_rate;
+
+ if (!clk)
+ return 0;
+@@ -1836,8 +1841,13 @@ static int clk_core_set_rate_nolock(stru
+ return -EBUSY;
+ }
+
++ 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);
+
+ clk->req_rate = req_rate;
+