aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh/clk/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sh/clk/core.c')
-rw-r--r--drivers/sh/clk/core.c112
1 files changed, 100 insertions, 12 deletions
diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c
index fd0d1b98901c..3f5e387ed564 100644
--- a/drivers/sh/clk/core.c
+++ b/drivers/sh/clk/core.c
@@ -90,8 +90,8 @@ struct clk_rate_round_data {
90static long clk_rate_round_helper(struct clk_rate_round_data *rounder) 90static long clk_rate_round_helper(struct clk_rate_round_data *rounder)
91{ 91{
92 unsigned long rate_error, rate_error_prev = ~0UL; 92 unsigned long rate_error, rate_error_prev = ~0UL;
93 unsigned long rate_best_fit = rounder->rate;
94 unsigned long highest, lowest, freq; 93 unsigned long highest, lowest, freq;
94 long rate_best_fit = -ENOENT;
95 int i; 95 int i;
96 96
97 highest = 0; 97 highest = 0;
@@ -146,7 +146,7 @@ long clk_rate_table_round(struct clk *clk,
146 }; 146 };
147 147
148 if (clk->nr_freqs < 1) 148 if (clk->nr_freqs < 1)
149 return 0; 149 return -ENOSYS;
150 150
151 return clk_rate_round_helper(&table_round); 151 return clk_rate_round_helper(&table_round);
152} 152}
@@ -418,8 +418,11 @@ int clk_register(struct clk *clk)
418 list_add(&clk->sibling, &root_clks); 418 list_add(&clk->sibling, &root_clks);
419 419
420 list_add(&clk->node, &clock_list); 420 list_add(&clk->node, &clock_list);
421
422#ifdef CONFIG_SH_CLK_CPG_LEGACY
421 if (clk->ops && clk->ops->init) 423 if (clk->ops && clk->ops->init)
422 clk->ops->init(clk); 424 clk->ops->init(clk);
425#endif
423 426
424out_unlock: 427out_unlock:
425 mutex_unlock(&clock_list_sem); 428 mutex_unlock(&clock_list_sem);
@@ -455,19 +458,13 @@ EXPORT_SYMBOL_GPL(clk_get_rate);
455 458
456int clk_set_rate(struct clk *clk, unsigned long rate) 459int clk_set_rate(struct clk *clk, unsigned long rate)
457{ 460{
458 return clk_set_rate_ex(clk, rate, 0);
459}
460EXPORT_SYMBOL_GPL(clk_set_rate);
461
462int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
463{
464 int ret = -EOPNOTSUPP; 461 int ret = -EOPNOTSUPP;
465 unsigned long flags; 462 unsigned long flags;
466 463
467 spin_lock_irqsave(&clock_lock, flags); 464 spin_lock_irqsave(&clock_lock, flags);
468 465
469 if (likely(clk->ops && clk->ops->set_rate)) { 466 if (likely(clk->ops && clk->ops->set_rate)) {
470 ret = clk->ops->set_rate(clk, rate, algo_id); 467 ret = clk->ops->set_rate(clk, rate);
471 if (ret != 0) 468 if (ret != 0)
472 goto out_unlock; 469 goto out_unlock;
473 } else { 470 } else {
@@ -485,7 +482,7 @@ out_unlock:
485 482
486 return ret; 483 return ret;
487} 484}
488EXPORT_SYMBOL_GPL(clk_set_rate_ex); 485EXPORT_SYMBOL_GPL(clk_set_rate);
489 486
490int clk_set_parent(struct clk *clk, struct clk *parent) 487int clk_set_parent(struct clk *clk, struct clk *parent)
491{ 488{
@@ -541,6 +538,98 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
541} 538}
542EXPORT_SYMBOL_GPL(clk_round_rate); 539EXPORT_SYMBOL_GPL(clk_round_rate);
543 540
541long clk_round_parent(struct clk *clk, unsigned long target,
542 unsigned long *best_freq, unsigned long *parent_freq,
543 unsigned int div_min, unsigned int div_max)
544{
545 struct cpufreq_frequency_table *freq, *best = NULL;
546 unsigned long error = ULONG_MAX, freq_high, freq_low, div;
547 struct clk *parent = clk_get_parent(clk);
548
549 if (!parent) {
550 *parent_freq = 0;
551 *best_freq = clk_round_rate(clk, target);
552 return abs(target - *best_freq);
553 }
554
555 for (freq = parent->freq_table; freq->frequency != CPUFREQ_TABLE_END;
556 freq++) {
557 if (freq->frequency == CPUFREQ_ENTRY_INVALID)
558 continue;
559
560 if (unlikely(freq->frequency / target <= div_min - 1)) {
561 unsigned long freq_max;
562
563 freq_max = (freq->frequency + div_min / 2) / div_min;
564 if (error > target - freq_max) {
565 error = target - freq_max;
566 best = freq;
567 if (best_freq)
568 *best_freq = freq_max;
569 }
570
571 pr_debug("too low freq %u, error %lu\n", freq->frequency,
572 target - freq_max);
573
574 if (!error)
575 break;
576
577 continue;
578 }
579
580 if (unlikely(freq->frequency / target >= div_max)) {
581 unsigned long freq_min;
582
583 freq_min = (freq->frequency + div_max / 2) / div_max;
584 if (error > freq_min - target) {
585 error = freq_min - target;
586 best = freq;
587 if (best_freq)
588 *best_freq = freq_min;
589 }
590
591 pr_debug("too high freq %u, error %lu\n", freq->frequency,
592 freq_min - target);
593
594 if (!error)
595 break;
596
597 continue;
598 }
599
600 div = freq->frequency / target;
601 freq_high = freq->frequency / div;
602 freq_low = freq->frequency / (div + 1);
603
604 if (freq_high - target < error) {
605 error = freq_high - target;
606 best = freq;
607 if (best_freq)
608 *best_freq = freq_high;
609 }
610
611 if (target - freq_low < error) {
612 error = target - freq_low;
613 best = freq;
614 if (best_freq)
615 *best_freq = freq_low;
616 }
617
618 pr_debug("%u / %lu = %lu, / %lu = %lu, best %lu, parent %u\n",
619 freq->frequency, div, freq_high, div + 1, freq_low,
620 *best_freq, best->frequency);
621
622 if (!error)
623 break;
624 }
625
626 if (parent_freq)
627 *parent_freq = best->frequency;
628
629 return error;
630}
631EXPORT_SYMBOL_GPL(clk_round_parent);
632
544#ifdef CONFIG_PM 633#ifdef CONFIG_PM
545static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) 634static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
546{ 635{
@@ -561,8 +650,7 @@ static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
561 clkp->ops->set_parent(clkp, 650 clkp->ops->set_parent(clkp,
562 clkp->parent); 651 clkp->parent);
563 if (likely(clkp->ops->set_rate)) 652 if (likely(clkp->ops->set_rate))
564 clkp->ops->set_rate(clkp, 653 clkp->ops->set_rate(clkp, rate);
565 rate, NO_CHANGE);
566 else if (likely(clkp->ops->recalc)) 654 else if (likely(clkp->ops->recalc))
567 clkp->rate = clkp->ops->recalc(clkp); 655 clkp->rate = clkp->ops->recalc(clkp);
568 } 656 }