diff options
author | Paul Walmsley <paul@pwsan.com> | 2014-07-25 08:11:15 -0400 |
---|---|---|
committer | Paul Walmsley <paul@pwsan.com> | 2014-07-25 08:11:15 -0400 |
commit | 0a26344440b5f9940c99112fdc3804f073b1f5a7 (patch) | |
tree | 82387918434b8eee49a76450044683e7d7a7bb59 /arch | |
parent | a497c3ba1d97fc69c1e78e7b96435ba8c2cb42ee (diff) |
ARM: OMAP2+: clock: allow omap2_dpll_round_rate() to round to next-lowest rate
Change the behavior of omap2_dpll_round_rate() to round to either the
exact rate requested, or the next lowest rate that the clock is able to
provide.
This is not an ideal fix, but is intended to provide a relatively safe
way for drivers to set PLL rates, until a better solution can be
implemented.
For the time being, omap3_noncore_dpll_set_rate() is still allowed to
set its rate to something other than what the caller requested; but will
warn when this occurs.
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/clkt_dpll.c | 28 | ||||
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 13 |
2 files changed, 32 insertions, 9 deletions
diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c index 332af927f4d3..85701142c5fc 100644 --- a/arch/arm/mach-omap2/clkt_dpll.c +++ b/arch/arm/mach-omap2/clkt_dpll.c | |||
@@ -293,10 +293,13 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, | |||
293 | { | 293 | { |
294 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | 294 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); |
295 | int m, n, r, scaled_max_m; | 295 | int m, n, r, scaled_max_m; |
296 | int min_delta_m = INT_MAX, min_delta_n = INT_MAX; | ||
296 | unsigned long scaled_rt_rp; | 297 | unsigned long scaled_rt_rp; |
297 | unsigned long new_rate = 0; | 298 | unsigned long new_rate = 0; |
298 | struct dpll_data *dd; | 299 | struct dpll_data *dd; |
299 | unsigned long ref_rate; | 300 | unsigned long ref_rate; |
301 | long delta; | ||
302 | long prev_min_delta = LONG_MAX; | ||
300 | const char *clk_name; | 303 | const char *clk_name; |
301 | 304 | ||
302 | if (!clk || !clk->dpll_data) | 305 | if (!clk || !clk->dpll_data) |
@@ -342,23 +345,34 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, | |||
342 | if (r == DPLL_MULT_UNDERFLOW) | 345 | if (r == DPLL_MULT_UNDERFLOW) |
343 | continue; | 346 | continue; |
344 | 347 | ||
348 | /* skip rates above our target rate */ | ||
349 | delta = target_rate - new_rate; | ||
350 | if (delta < 0) | ||
351 | continue; | ||
352 | |||
353 | if (delta < prev_min_delta) { | ||
354 | prev_min_delta = delta; | ||
355 | min_delta_m = m; | ||
356 | min_delta_n = n; | ||
357 | } | ||
358 | |||
345 | pr_debug("clock: %s: m = %d: n = %d: new_rate = %lu\n", | 359 | pr_debug("clock: %s: m = %d: n = %d: new_rate = %lu\n", |
346 | clk_name, m, n, new_rate); | 360 | clk_name, m, n, new_rate); |
347 | 361 | ||
348 | if (target_rate == new_rate) { | 362 | if (delta == 0) |
349 | dd->last_rounded_m = m; | ||
350 | dd->last_rounded_n = n; | ||
351 | dd->last_rounded_rate = target_rate; | ||
352 | break; | 363 | break; |
353 | } | ||
354 | } | 364 | } |
355 | 365 | ||
356 | if (target_rate != new_rate) { | 366 | if (prev_min_delta == LONG_MAX) { |
357 | pr_debug("clock: %s: cannot round to rate %lu\n", | 367 | pr_debug("clock: %s: cannot round to rate %lu\n", |
358 | clk_name, target_rate); | 368 | clk_name, target_rate); |
359 | return ~0; | 369 | return ~0; |
360 | } | 370 | } |
361 | 371 | ||
362 | return target_rate; | 372 | dd->last_rounded_m = min_delta_m; |
373 | dd->last_rounded_n = min_delta_n; | ||
374 | dd->last_rounded_rate = target_rate - prev_min_delta; | ||
375 | |||
376 | return dd->last_rounded_rate; | ||
363 | } | 377 | } |
364 | 378 | ||
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index 6d7ba37e2257..b6860bfb05b3 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c | |||
@@ -478,6 +478,7 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, | |||
478 | { | 478 | { |
479 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | 479 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); |
480 | struct clk *new_parent = NULL; | 480 | struct clk *new_parent = NULL; |
481 | unsigned long rrate; | ||
481 | u16 freqsel = 0; | 482 | u16 freqsel = 0; |
482 | struct dpll_data *dd; | 483 | struct dpll_data *dd; |
483 | int ret; | 484 | int ret; |
@@ -505,8 +506,16 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, | |||
505 | __clk_prepare(dd->clk_ref); | 506 | __clk_prepare(dd->clk_ref); |
506 | clk_enable(dd->clk_ref); | 507 | clk_enable(dd->clk_ref); |
507 | 508 | ||
508 | if (dd->last_rounded_rate != rate) | 509 | /* XXX this check is probably pointless in the CCF context */ |
509 | rate = __clk_round_rate(hw->clk, rate); | 510 | if (dd->last_rounded_rate != rate) { |
511 | rrate = __clk_round_rate(hw->clk, rate); | ||
512 | if (rrate != rate) { | ||
513 | pr_warn("%s: %s: final rate %lu does not match desired rate %lu\n", | ||
514 | __func__, __clk_get_name(hw->clk), | ||
515 | rrate, rate); | ||
516 | rate = rrate; | ||
517 | } | ||
518 | } | ||
510 | 519 | ||
511 | if (dd->last_rounded_rate == 0) | 520 | if (dd->last_rounded_rate == 0) |
512 | return -EINVAL; | 521 | return -EINVAL; |