aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2010-11-02 07:27:24 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-11-07 19:35:26 -0500
commit6af26c6c99f01e810f9944543df810e320284aa3 (patch)
tree9eaca6f86d322b700484e13cd4ebb0b3f6fc0c97 /drivers/sh
parent5aefa34fada9d17a00635516688de34702451708 (diff)
sh: add clk_round_parent() to optimize parent clock rate
Sometimes it is possible and reasonable to adjust the parent clock rate to improve precision of the child clock, e.g., if the child clock has no siblings. clk_round_parent() is a new addition to the SH clock-framework API, that implements such an optimization for child clocks with divisors, taking all integer values in a range. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/sh')
-rw-r--r--drivers/sh/clk/core.c75
1 files changed, 75 insertions, 0 deletions
diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c
index 861144360d89..b3840597ad6e 100644
--- a/drivers/sh/clk/core.c
+++ b/drivers/sh/clk/core.c
@@ -541,6 +541,81 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
541} 541}
542EXPORT_SYMBOL_GPL(clk_round_rate); 542EXPORT_SYMBOL_GPL(clk_round_rate);
543 543
544long clk_round_parent(struct clk *clk, unsigned long target,
545 unsigned long *best_freq, unsigned long *parent_freq,
546 unsigned int div_min, unsigned int div_max)
547{
548 struct cpufreq_frequency_table *freq, *best = NULL;
549 unsigned long error = ULONG_MAX, freq_high, freq_low, div;
550 struct clk *parent = clk_get_parent(clk);
551
552 if (!parent) {
553 *parent_freq = 0;
554 *best_freq = clk_round_rate(clk, target);
555 return abs(target - *best_freq);
556 }
557
558 for (freq = parent->freq_table; freq->frequency != CPUFREQ_TABLE_END;
559 freq++) {
560 if (freq->frequency == CPUFREQ_ENTRY_INVALID)
561 continue;
562
563 if (unlikely(freq->frequency / target <= div_min - 1)) {
564 unsigned long freq_max = (freq->frequency + div_min / 2) / div_min;
565 if (error > target - freq_max) {
566 error = target - freq_max;
567 best = freq;
568 if (best_freq)
569 *best_freq = freq_max;
570 }
571 pr_debug("too low freq %lu, error %lu\n", freq->frequency, target - freq_max);
572 if (!error)
573 break;
574 continue;
575 }
576
577 if (unlikely(freq->frequency / target >= div_max)) {
578 unsigned long freq_min = (freq->frequency + div_max / 2) / div_max;
579 if (error > freq_min - target) {
580 error = freq_min - target;
581 best = freq;
582 if (best_freq)
583 *best_freq = freq_min;
584 }
585 pr_debug("too high freq %lu, error %lu\n", freq->frequency, freq_min - target);
586 if (!error)
587 break;
588 continue;
589 }
590
591
592 div = freq->frequency / target;
593 freq_high = freq->frequency / div;
594 freq_low = freq->frequency / (div + 1);
595 if (freq_high - target < error) {
596 error = freq_high - target;
597 best = freq;
598 if (best_freq)
599 *best_freq = freq_high;
600 }
601 if (target - freq_low < error) {
602 error = target - freq_low;
603 best = freq;
604 if (best_freq)
605 *best_freq = freq_low;
606 }
607 pr_debug("%u / %lu = %lu, / %lu = %lu, best %lu, parent %u\n",
608 freq->frequency, div, freq_high, div + 1, freq_low,
609 *best_freq, best->frequency);
610 if (!error)
611 break;
612 }
613 if (parent_freq)
614 *parent_freq = best->frequency;
615 return error;
616}
617EXPORT_SYMBOL_GPL(clk_round_parent);
618
544#ifdef CONFIG_PM 619#ifdef CONFIG_PM
545static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) 620static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
546{ 621{