aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/sh/clk/core.c75
-rw-r--r--include/linux/sh_clk.h4
2 files changed, 79 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{
diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h
index 4dca992f3093..cea0c38e7a63 100644
--- a/include/linux/sh_clk.h
+++ b/include/linux/sh_clk.h
@@ -122,6 +122,10 @@ int clk_rate_table_find(struct clk *clk,
122long clk_rate_div_range_round(struct clk *clk, unsigned int div_min, 122long clk_rate_div_range_round(struct clk *clk, unsigned int div_min,
123 unsigned int div_max, unsigned long rate); 123 unsigned int div_max, unsigned long rate);
124 124
125long clk_round_parent(struct clk *clk, unsigned long target,
126 unsigned long *best_freq, unsigned long *parent_freq,
127 unsigned int div_min, unsigned int div_max);
128
125#define SH_CLK_MSTP32(_parent, _enable_reg, _enable_bit, _flags) \ 129#define SH_CLK_MSTP32(_parent, _enable_reg, _enable_bit, _flags) \
126{ \ 130{ \
127 .parent = _parent, \ 131 .parent = _parent, \