Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Doug Anderson and committed by
Heiko Stuebner
9c030ea7 f79c3c01

+13 -50
+13 -50
drivers/clk/rockchip/clk-pll.c
··· 34 34 const struct clk_ops *pll_mux_ops; 35 35 36 36 struct notifier_block clk_nb; 37 - bool rate_change_remuxed; 38 37 39 38 void __iomem *reg_base; 40 39 int lock_offset; ··· 108 109 } 109 110 110 111 /** 111 - * Set pll mux when changing the pll rate. 112 - * This makes sure to move the pll mux away from the actual pll before 113 - * changing its rate and back to the original parent after the change. 114 - */ 115 - static int rockchip_pll_notifier_cb(struct notifier_block *nb, 116 - unsigned long event, void *data) 117 - { 118 - struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb); 119 - struct clk_mux *pll_mux = &pll->pll_mux; 120 - const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; 121 - int cur_parent; 122 - 123 - switch (event) { 124 - case PRE_RATE_CHANGE: 125 - cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); 126 - if (cur_parent == PLL_MODE_NORM) { 127 - pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); 128 - pll->rate_change_remuxed = 1; 129 - } 130 - break; 131 - case POST_RATE_CHANGE: 132 - if (pll->rate_change_remuxed) { 133 - pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); 134 - pll->rate_change_remuxed = 0; 135 - } 136 - break; 137 - } 138 - 139 - return NOTIFY_OK; 140 - } 141 - 142 - /** 143 112 * PLL used in RK3066, RK3188 and RK3288 144 113 */ 145 114 ··· 161 194 const struct rockchip_pll_rate_table *rate; 162 195 unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); 163 196 struct regmap *grf = rockchip_clk_get_grf(); 197 + struct clk_mux *pll_mux = &pll->pll_mux; 198 + const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; 199 + int rate_change_remuxed = 0; 200 + int cur_parent; 164 201 int ret; 165 202 166 203 if (IS_ERR(grf)) { ··· 186 215 187 216 pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", 188 217 __func__, rate->rate, rate->nr, rate->no, rate->nf); 218 + 219 + cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); 220 + if (cur_parent == PLL_MODE_NORM) { 221 + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); 222 + rate_change_remuxed = 1; 223 + } 189 224 190 225 /* enter reset mode */ 191 226 writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0), ··· 223 246 __func__, old_rate); 224 247 rockchip_rk3066_pll_set_rate(hw, old_rate, prate); 225 248 } 249 + 250 + if (rate_change_remuxed) 251 + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); 226 252 227 253 return ret; 228 254 } ··· 290 310 struct clk_mux *pll_mux; 291 311 struct clk *pll_clk, *mux_clk; 292 312 char pll_name[20]; 293 - int ret; 294 313 295 314 if (num_parents != 2) { 296 315 pr_err("%s: needs two parent clocks\n", __func__); ··· 346 367 pll->lock_offset = grf_lock_offset; 347 368 pll->lock_shift = lock_shift; 348 369 pll->lock = lock; 349 - pll->clk_nb.notifier_call = rockchip_pll_notifier_cb; 350 370 351 371 pll_clk = clk_register(NULL, &pll->hw); 352 372 if (IS_ERR(pll_clk)) { ··· 353 375 __func__, name, PTR_ERR(pll_clk)); 354 376 mux_clk = pll_clk; 355 377 goto err_pll; 356 - } 357 - 358 - ret = clk_notifier_register(pll_clk, &pll->clk_nb); 359 - if (ret) { 360 - pr_err("%s: failed to register clock notifier for %s : %d\n", 361 - __func__, name, ret); 362 - mux_clk = ERR_PTR(ret); 363 - goto err_pll_notifier; 364 378 } 365 379 366 380 /* create the mux on top of the real pll */ ··· 387 417 return mux_clk; 388 418 389 419 err_mux: 390 - ret = clk_notifier_unregister(pll_clk, &pll->clk_nb); 391 - if (ret) { 392 - pr_err("%s: could not unregister clock notifier in error path : %d\n", 393 - __func__, ret); 394 - return mux_clk; 395 - } 396 - err_pll_notifier: 397 420 clk_unregister(pll_clk); 398 421 err_pll: 399 422 kfree(pll);