aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2014-07-25 08:11:15 -0400
committerPaul Walmsley <paul@pwsan.com>2014-07-25 08:11:15 -0400
commit0a26344440b5f9940c99112fdc3804f073b1f5a7 (patch)
tree82387918434b8eee49a76450044683e7d7a7bb59 /arch
parenta497c3ba1d97fc69c1e78e7b96435ba8c2cb42ee (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.c28
-rw-r--r--arch/arm/mach-omap2/dpll3xxx.c13
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;