diff options
Diffstat (limited to 'drivers/sh/clk/core.c')
-rw-r--r-- | drivers/sh/clk/core.c | 112 |
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 { | |||
90 | static long clk_rate_round_helper(struct clk_rate_round_data *rounder) | 90 | static 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 | ||
424 | out_unlock: | 427 | out_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 | ||
456 | int clk_set_rate(struct clk *clk, unsigned long rate) | 459 | int clk_set_rate(struct clk *clk, unsigned long rate) |
457 | { | 460 | { |
458 | return clk_set_rate_ex(clk, rate, 0); | ||
459 | } | ||
460 | EXPORT_SYMBOL_GPL(clk_set_rate); | ||
461 | |||
462 | int 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 | } |
488 | EXPORT_SYMBOL_GPL(clk_set_rate_ex); | 485 | EXPORT_SYMBOL_GPL(clk_set_rate); |
489 | 486 | ||
490 | int clk_set_parent(struct clk *clk, struct clk *parent) | 487 | int 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 | } |
542 | EXPORT_SYMBOL_GPL(clk_round_rate); | 539 | EXPORT_SYMBOL_GPL(clk_round_rate); |
543 | 540 | ||
541 | long 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 | } | ||
631 | EXPORT_SYMBOL_GPL(clk_round_parent); | ||
632 | |||
544 | #ifdef CONFIG_PM | 633 | #ifdef CONFIG_PM |
545 | static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) | 634 | static 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 | } |