aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Anderson <dianders@chromium.org>2014-09-16 00:07:57 -0400
committerHeiko Stuebner <heiko@sntech.de>2014-09-27 11:57:04 -0400
commit9c030ea70b6a7eb2f7ee273b1ddd2282b053724f (patch)
tree561c3d46d28366d13c0f20112afaa34793e193ac
parentf79c3c015485374b4f453932c45fbcb80a75a1a9 (diff)
clk: rockchip: change pll rate without a clk-notifier
The Rockchip PLL code switches into slow mode (AKA bypass more AKA 24MHz mode) before actually changing the PLL. This keeps anyone from using the PLL while it's changing. However, in all known Rockchip SoCs nobody should ever see the 24MHz when changing the PLL supplying the armclk because we should reparent children to an alternate (faster than 24MHz) PLL. One problem is that the code to switch to an alternate parent was running in PRE_RATE_CHANGE. ...and the code to switch to slow mode was _also_ running in PRE_RATE_CHANGE. That meant there was no real guarantee that we would switch to an alternate parent before switching to 24MHz mode. Let's move the switch to "slow mode" straight into rockchip_rk3066_pll_set_rate(). That means we're guaranteed that the 24MHz is really a last-resort. Note that without this change on real systems we were the code to switch to an alternate parent at 24MHz. In some older versions of that code we'd appy a (temporary) / 5 to the 24MHz causing us to run at 4.8MHz. That wasn't enough to service USB interrupts in some cases and could lead to a system hang. Signed-off-by: Doug Anderson <dianders@chromium.org> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
-rw-r--r--drivers/clk/rockchip/clk-pll.c63
1 files changed, 13 insertions, 50 deletions
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index f2a1c7abf4d9..a3e886a38480 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -34,7 +34,6 @@ struct rockchip_clk_pll {
34 const struct clk_ops *pll_mux_ops; 34 const struct clk_ops *pll_mux_ops;
35 35
36 struct notifier_block clk_nb; 36 struct notifier_block clk_nb;
37 bool rate_change_remuxed;
38 37
39 void __iomem *reg_base; 38 void __iomem *reg_base;
40 int lock_offset; 39 int lock_offset;
@@ -109,38 +108,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
109} 108}
110 109
111/** 110/**
112 * Set pll mux when changing the pll rate.
113 * This makes sure to move the pll mux away from the actual pll before
114 * changing its rate and back to the original parent after the change.
115 */
116static int rockchip_pll_notifier_cb(struct notifier_block *nb,
117 unsigned long event, void *data)
118{
119 struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb);
120 struct clk_mux *pll_mux = &pll->pll_mux;
121 const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
122 int cur_parent;
123
124 switch (event) {
125 case PRE_RATE_CHANGE:
126 cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
127 if (cur_parent == PLL_MODE_NORM) {
128 pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
129 pll->rate_change_remuxed = 1;
130 }
131 break;
132 case POST_RATE_CHANGE:
133 if (pll->rate_change_remuxed) {
134 pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
135 pll->rate_change_remuxed = 0;
136 }
137 break;
138 }
139
140 return NOTIFY_OK;
141}
142
143/**
144 * PLL used in RK3066, RK3188 and RK3288 111 * PLL used in RK3066, RK3188 and RK3288
145 */ 112 */
146 113
@@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
194 const struct rockchip_pll_rate_table *rate; 161 const struct rockchip_pll_rate_table *rate;
195 unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); 162 unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
196 struct regmap *grf = rockchip_clk_get_grf(); 163 struct regmap *grf = rockchip_clk_get_grf();
164 struct clk_mux *pll_mux = &pll->pll_mux;
165 const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
166 int rate_change_remuxed = 0;
167 int cur_parent;
197 int ret; 168 int ret;
198 169
199 if (IS_ERR(grf)) { 170 if (IS_ERR(grf)) {
@@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
216 pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", 187 pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
217 __func__, rate->rate, rate->nr, rate->no, rate->nf); 188 __func__, rate->rate, rate->nr, rate->no, rate->nf);
218 189
190 cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
191 if (cur_parent == PLL_MODE_NORM) {
192 pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
193 rate_change_remuxed = 1;
194 }
195
219 /* enter reset mode */ 196 /* enter reset mode */
220 writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0), 197 writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
221 pll->reg_base + RK3066_PLLCON(3)); 198 pll->reg_base + RK3066_PLLCON(3));
@@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
247 rockchip_rk3066_pll_set_rate(hw, old_rate, prate); 224 rockchip_rk3066_pll_set_rate(hw, old_rate, prate);
248 } 225 }
249 226
227 if (rate_change_remuxed)
228 pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
229
250 return ret; 230 return ret;
251} 231}
252 232
@@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
310 struct clk_mux *pll_mux; 290 struct clk_mux *pll_mux;
311 struct clk *pll_clk, *mux_clk; 291 struct clk *pll_clk, *mux_clk;
312 char pll_name[20]; 292 char pll_name[20];
313 int ret;
314 293
315 if (num_parents != 2) { 294 if (num_parents != 2) {
316 pr_err("%s: needs two parent clocks\n", __func__); 295 pr_err("%s: needs two parent clocks\n", __func__);
@@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
367 pll->lock_offset = grf_lock_offset; 346 pll->lock_offset = grf_lock_offset;
368 pll->lock_shift = lock_shift; 347 pll->lock_shift = lock_shift;
369 pll->lock = lock; 348 pll->lock = lock;
370 pll->clk_nb.notifier_call = rockchip_pll_notifier_cb;
371 349
372 pll_clk = clk_register(NULL, &pll->hw); 350 pll_clk = clk_register(NULL, &pll->hw);
373 if (IS_ERR(pll_clk)) { 351 if (IS_ERR(pll_clk)) {
@@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
377 goto err_pll; 355 goto err_pll;
378 } 356 }
379 357
380 ret = clk_notifier_register(pll_clk, &pll->clk_nb);
381 if (ret) {
382 pr_err("%s: failed to register clock notifier for %s : %d\n",
383 __func__, name, ret);
384 mux_clk = ERR_PTR(ret);
385 goto err_pll_notifier;
386 }
387
388 /* create the mux on top of the real pll */ 358 /* create the mux on top of the real pll */
389 pll->pll_mux_ops = &clk_mux_ops; 359 pll->pll_mux_ops = &clk_mux_ops;
390 pll_mux = &pll->pll_mux; 360 pll_mux = &pll->pll_mux;
@@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
417 return mux_clk; 387 return mux_clk;
418 388
419err_mux: 389err_mux:
420 ret = clk_notifier_unregister(pll_clk, &pll->clk_nb);
421 if (ret) {
422 pr_err("%s: could not unregister clock notifier in error path : %d\n",
423 __func__, ret);
424 return mux_clk;
425 }
426err_pll_notifier:
427 clk_unregister(pll_clk); 390 clk_unregister(pll_clk);
428err_pll: 391err_pll:
429 kfree(pll); 392 kfree(pll);