diff options
author | Lori Hikichi <lhikichi@broadcom.com> | 2017-08-14 15:00:40 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2017-12-28 17:53:36 -0500 |
commit | f3f739c93c42f50870a75af6c9bf001854313c25 (patch) | |
tree | c64e9eec2cd8cf467fec71561a9e830984656ecf | |
parent | 85151a6b0b331f7bc4d9534d6138bfdd3e206b0d (diff) |
clk: iproc: Allow plls to do minor rate changes without reset
The iproc plls are capable of doing small rate changes without the
need for a full reset and re-lock procedure. This feature will
allow for small tweaks to the PLL rate to occur smoothly.
Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r-- | drivers/clk/bcm/clk-iproc-pll.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c index 7df010b2edcd..ab10819e2c09 100644 --- a/drivers/clk/bcm/clk-iproc-pll.c +++ b/drivers/clk/bcm/clk-iproc-pll.c | |||
@@ -285,6 +285,40 @@ static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, | |||
285 | iproc_pll_write(pll, pll->control_base, reset->offset, val); | 285 | iproc_pll_write(pll, pll->control_base, reset->offset, val); |
286 | } | 286 | } |
287 | 287 | ||
288 | /* | ||
289 | * Determines if the change to be applied to the PLL is minor (just an update | ||
290 | * or the fractional divider). If so, then we can avoid going through a | ||
291 | * disruptive reset and lock sequence. | ||
292 | */ | ||
293 | static bool pll_fractional_change_only(struct iproc_pll *pll, | ||
294 | struct iproc_pll_vco_param *vco) | ||
295 | { | ||
296 | const struct iproc_pll_ctrl *ctrl = pll->ctrl; | ||
297 | u32 val; | ||
298 | u32 ndiv_int; | ||
299 | unsigned int pdiv; | ||
300 | |||
301 | /* PLL needs to be locked */ | ||
302 | val = readl(pll->status_base + ctrl->status.offset); | ||
303 | if ((val & (1 << ctrl->status.shift)) == 0) | ||
304 | return false; | ||
305 | |||
306 | val = readl(pll->control_base + ctrl->ndiv_int.offset); | ||
307 | ndiv_int = (val >> ctrl->ndiv_int.shift) & | ||
308 | bit_mask(ctrl->ndiv_int.width); | ||
309 | |||
310 | if (ndiv_int != vco->ndiv_int) | ||
311 | return false; | ||
312 | |||
313 | val = readl(pll->control_base + ctrl->pdiv.offset); | ||
314 | pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); | ||
315 | |||
316 | if (pdiv != vco->pdiv) | ||
317 | return false; | ||
318 | |||
319 | return true; | ||
320 | } | ||
321 | |||
288 | static int pll_set_rate(struct iproc_clk *clk, struct iproc_pll_vco_param *vco, | 322 | static int pll_set_rate(struct iproc_clk *clk, struct iproc_pll_vco_param *vco, |
289 | unsigned long parent_rate) | 323 | unsigned long parent_rate) |
290 | { | 324 | { |
@@ -333,6 +367,19 @@ static int pll_set_rate(struct iproc_clk *clk, struct iproc_pll_vco_param *vco, | |||
333 | return ret; | 367 | return ret; |
334 | } | 368 | } |
335 | 369 | ||
370 | if (pll_fractional_change_only(clk->pll, vco)) { | ||
371 | /* program fractional part of NDIV */ | ||
372 | if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { | ||
373 | val = readl(pll->control_base + ctrl->ndiv_frac.offset); | ||
374 | val &= ~(bit_mask(ctrl->ndiv_frac.width) << | ||
375 | ctrl->ndiv_frac.shift); | ||
376 | val |= vco->ndiv_frac << ctrl->ndiv_frac.shift; | ||
377 | iproc_pll_write(pll, pll->control_base, | ||
378 | ctrl->ndiv_frac.offset, val); | ||
379 | return 0; | ||
380 | } | ||
381 | } | ||
382 | |||
336 | /* put PLL in reset */ | 383 | /* put PLL in reset */ |
337 | __pll_put_in_reset(pll); | 384 | __pll_put_in_reset(pll); |
338 | 385 | ||