ipq806x: Add support for IPQ806x chip family
[openwrt/openwrt.git] / target / linux / ipq806x / patches / 0126-clk-Add-safe-switch-hook.patch
1 From b7b3ceec506179d59e46e3a8ea8b370872cc7fcb Mon Sep 17 00:00:00 2001
2 From: Stephen Boyd <sboyd@codeaurora.org>
3 Date: Mon, 31 Mar 2014 16:52:29 -0700
4 Subject: [PATCH 126/182] clk: Add safe switch hook
5
6 Sometimes clocks can't accept their parent source turning off
7 while the source is reprogrammed to a different rate. Most
8 notably CPU clocks require a way to switch away from the current
9 PLL they're running on, reprogram that PLL to a new rate, and
10 then switch back to the PLL with the new rate once they're done.
11 Add a hook that drivers can implement allowing them to return a
12 'safe parent' that they can switch their parent to while the
13 upstream source is reprogrammed to support this.
14
15 Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
16 ---
17 drivers/clk/clk.c | 53 ++++++++++++++++++++++++++++++++++++------
18 include/linux/clk-private.h | 2 ++
19 include/linux/clk-provider.h | 1 +
20 3 files changed, 49 insertions(+), 7 deletions(-)
21
22 diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
23 index b94a311..0582068 100644
24 --- a/drivers/clk/clk.c
25 +++ b/drivers/clk/clk.c
26 @@ -1356,6 +1356,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
27 struct clk *new_parent, u8 p_index)
28 {
29 struct clk *child;
30 + struct clk *parent;
31
32 clk->new_rate = new_rate;
33 clk->new_parent = new_parent;
34 @@ -1365,6 +1366,17 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
35 if (new_parent && new_parent != clk->parent)
36 new_parent->new_child = clk;
37
38 + if (clk->ops->get_safe_parent) {
39 + parent = clk->ops->get_safe_parent(clk->hw);
40 + if (parent) {
41 + p_index = clk_fetch_parent_index(clk, parent);
42 + clk->safe_parent_index = p_index;
43 + clk->safe_parent = parent;
44 + }
45 + } else {
46 + clk->safe_parent = NULL;
47 + }
48 +
49 hlist_for_each_entry(child, &clk->children, child_node) {
50 if (child->ops->recalc_rate)
51 child->new_rate = child->ops->recalc_rate(child->hw, new_rate);
52 @@ -1450,14 +1462,42 @@ out:
53 static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
54 {
55 struct clk *child, *tmp_clk, *fail_clk = NULL;
56 + struct clk *old_parent;
57 int ret = NOTIFY_DONE;
58
59 - if (clk->rate == clk->new_rate)
60 + if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE)
61 return NULL;
62
63 + switch (event) {
64 + case PRE_RATE_CHANGE:
65 + if (clk->safe_parent)
66 + clk->ops->set_parent(clk->hw, clk->safe_parent_index);
67 + break;
68 + case POST_RATE_CHANGE:
69 + if (clk->safe_parent) {
70 + old_parent = __clk_set_parent_before(clk,
71 + clk->new_parent);
72 + if (clk->ops->set_rate_and_parent) {
73 + clk->ops->set_rate_and_parent(clk->hw,
74 + clk->new_rate,
75 + clk->new_parent ?
76 + clk->new_parent->rate : 0,
77 + clk->new_parent_index);
78 + } else if (clk->ops->set_parent) {
79 + clk->ops->set_parent(clk->hw,
80 + clk->new_parent_index);
81 + }
82 + __clk_set_parent_after(clk, clk->new_parent,
83 + old_parent);
84 + }
85 + break;
86 + }
87 +
88 if (clk->notifier_count) {
89 - ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
90 - if (ret & NOTIFY_STOP_MASK)
91 + if (event != POST_RATE_CHANGE)
92 + ret = __clk_notify(clk, event, clk->rate,
93 + clk->new_rate);
94 + if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
95 fail_clk = clk;
96 }
97
98 @@ -1499,7 +1539,8 @@ static void clk_change_rate(struct clk *clk)
99 else if (clk->parent)
100 best_parent_rate = clk->parent->rate;
101
102 - if (clk->new_parent && clk->new_parent != clk->parent) {
103 + if (clk->new_parent && clk->new_parent != clk->parent &&
104 + !clk->safe_parent) {
105 old_parent = __clk_set_parent_before(clk, clk->new_parent);
106
107 if (clk->ops->set_rate_and_parent) {
108 @@ -1522,9 +1563,6 @@ static void clk_change_rate(struct clk *clk)
109 else
110 clk->rate = best_parent_rate;
111
112 - if (clk->notifier_count && old_rate != clk->rate)
113 - __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
114 -
115 hlist_for_each_entry(child, &clk->children, child_node) {
116 /* Skip children who will be reparented to another clock */
117 if (child->new_parent && child->new_parent != clk)
118 @@ -1598,6 +1636,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
119 /* change the rates */
120 clk_change_rate(top);
121
122 + clk_propagate_rate_change(top, POST_RATE_CHANGE);
123 out:
124 clk_prepare_unlock();
125
126 diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
127 index efbf70b..f48684a 100644
128 --- a/include/linux/clk-private.h
129 +++ b/include/linux/clk-private.h
130 @@ -38,8 +38,10 @@ struct clk {
131 struct clk **parents;
132 u8 num_parents;
133 u8 new_parent_index;
134 + u8 safe_parent_index;
135 unsigned long rate;
136 unsigned long new_rate;
137 + struct clk *safe_parent;
138 struct clk *new_parent;
139 struct clk *new_child;
140 unsigned long flags;
141 diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
142 index 939533d..300fcb8 100644
143 --- a/include/linux/clk-provider.h
144 +++ b/include/linux/clk-provider.h
145 @@ -157,6 +157,7 @@ struct clk_ops {
146 struct clk **best_parent_clk);
147 int (*set_parent)(struct clk_hw *hw, u8 index);
148 u8 (*get_parent)(struct clk_hw *hw);
149 + struct clk *(*get_safe_parent)(struct clk_hw *hw);
150 int (*set_rate)(struct clk_hw *hw, unsigned long,
151 unsigned long);
152 int (*set_rate_and_parent)(struct clk_hw *hw,
153 --
154 1.7.10.4
155