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 | ||
