diff options
author | Tero Kristo <t-kristo@ti.com> | 2014-10-03 09:57:11 -0400 |
---|---|---|
committer | Paul Walmsley <paul@pwsan.com> | 2014-11-13 11:26:16 -0500 |
commit | d539efa37f1f789339699c941e72e320d12d5f28 (patch) | |
tree | 8256b57b68bd582e1d8440be29cb6bb74d53abc2 | |
parent | f0d2f68a6387867c4748bc94c9b0165a49701204 (diff) |
ARM: OMAP3: clock: add new rate changing logic support for noncore DPLLs
Currently, DPLL code hides the re-parenting within its internals, which
is wrong. This needs to be exposed to the common clock code via
determine_rate and set_rate_and_parent APIs. This patch adds support
for these, which will be taken into use in the following patches.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 147 | ||||
-rw-r--r-- | include/linux/clk/ti.h | 9 |
2 files changed, 156 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index ac3d789ac3cd..cfe7c30235d1 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c | |||
@@ -546,6 +546,153 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, | |||
546 | return 0; | 546 | return 0; |
547 | } | 547 | } |
548 | 548 | ||
549 | /** | ||
550 | * omap3_noncore_dpll_determine_rate - determine rate for a DPLL | ||
551 | * @hw: pointer to the clock to determine rate for | ||
552 | * @rate: target rate for the DPLL | ||
553 | * @best_parent_rate: pointer for returning best parent rate | ||
554 | * @best_parent_clk: pointer for returning best parent clock | ||
555 | * | ||
556 | * Determines which DPLL mode to use for reaching a desired target rate. | ||
557 | * Checks whether the DPLL shall be in bypass or locked mode, and if | ||
558 | * locked, calculates the M,N values for the DPLL via round-rate. | ||
559 | * Returns a positive clock rate with success, negative error value | ||
560 | * in failure. | ||
561 | */ | ||
562 | long omap3_noncore_dpll_determine_rate(struct clk_hw *hw, unsigned long rate, | ||
563 | unsigned long *best_parent_rate, | ||
564 | struct clk **best_parent_clk) | ||
565 | { | ||
566 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
567 | struct dpll_data *dd; | ||
568 | |||
569 | if (!hw || !rate) | ||
570 | return -EINVAL; | ||
571 | |||
572 | dd = clk->dpll_data; | ||
573 | if (!dd) | ||
574 | return -EINVAL; | ||
575 | |||
576 | if (__clk_get_rate(dd->clk_bypass) == rate && | ||
577 | (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { | ||
578 | *best_parent_clk = dd->clk_bypass; | ||
579 | } else { | ||
580 | rate = omap2_dpll_round_rate(hw, rate, best_parent_rate); | ||
581 | *best_parent_clk = dd->clk_ref; | ||
582 | } | ||
583 | |||
584 | *best_parent_rate = rate; | ||
585 | |||
586 | return rate; | ||
587 | } | ||
588 | |||
589 | /** | ||
590 | * omap3_noncore_dpll_set_parent - set parent for a DPLL clock | ||
591 | * @hw: pointer to the clock to set parent for | ||
592 | * @index: parent index to select | ||
593 | * | ||
594 | * Sets parent for a DPLL clock. This sets the DPLL into bypass or | ||
595 | * locked mode. Returns 0 with success, negative error value otherwise. | ||
596 | */ | ||
597 | int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index) | ||
598 | { | ||
599 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
600 | int ret; | ||
601 | |||
602 | if (!hw) | ||
603 | return -EINVAL; | ||
604 | |||
605 | if (index) | ||
606 | ret = _omap3_noncore_dpll_bypass(clk); | ||
607 | else | ||
608 | ret = _omap3_noncore_dpll_lock(clk); | ||
609 | |||
610 | return ret; | ||
611 | } | ||
612 | |||
613 | /** | ||
614 | * omap3_noncore_dpll_set_rate_new - set rate for a DPLL clock | ||
615 | * @hw: pointer to the clock to set parent for | ||
616 | * @rate: target rate for the clock | ||
617 | * @parent_rate: rate of the parent clock | ||
618 | * | ||
619 | * Sets rate for a DPLL clock. First checks if the clock parent is | ||
620 | * reference clock (in bypass mode, the rate of the clock can't be | ||
621 | * changed) and proceeds with the rate change operation. Returns 0 | ||
622 | * with success, negative error value otherwise. | ||
623 | */ | ||
624 | static int omap3_noncore_dpll_set_rate_new(struct clk_hw *hw, | ||
625 | unsigned long rate, | ||
626 | unsigned long parent_rate) | ||
627 | { | ||
628 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
629 | struct dpll_data *dd; | ||
630 | u16 freqsel = 0; | ||
631 | int ret; | ||
632 | |||
633 | if (!hw || !rate) | ||
634 | return -EINVAL; | ||
635 | |||
636 | dd = clk->dpll_data; | ||
637 | if (!dd) | ||
638 | return -EINVAL; | ||
639 | |||
640 | if (__clk_get_parent(hw->clk) != dd->clk_ref) | ||
641 | return -EINVAL; | ||
642 | |||
643 | if (dd->last_rounded_rate == 0) | ||
644 | return -EINVAL; | ||
645 | |||
646 | /* Freqsel is available only on OMAP343X devices */ | ||
647 | if (ti_clk_features.flags & TI_CLK_DPLL_HAS_FREQSEL) { | ||
648 | freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); | ||
649 | WARN_ON(!freqsel); | ||
650 | } | ||
651 | |||
652 | pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, | ||
653 | __clk_get_name(hw->clk), rate); | ||
654 | |||
655 | ret = omap3_noncore_dpll_program(clk, freqsel); | ||
656 | |||
657 | return ret; | ||
658 | } | ||
659 | |||
660 | /** | ||
661 | * omap3_noncore_dpll_set_rate_and_parent - set rate and parent for a DPLL clock | ||
662 | * @hw: pointer to the clock to set rate and parent for | ||
663 | * @rate: target rate for the DPLL | ||
664 | * @parent_rate: clock rate of the DPLL parent | ||
665 | * @index: new parent index for the DPLL, 0 - reference, 1 - bypass | ||
666 | * | ||
667 | * Sets rate and parent for a DPLL clock. If new parent is the bypass | ||
668 | * clock, only selects the parent. Otherwise proceeds with a rate | ||
669 | * change, as this will effectively also change the parent as the | ||
670 | * DPLL is put into locked mode. Returns 0 with success, negative error | ||
671 | * value otherwise. | ||
672 | */ | ||
673 | int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw, | ||
674 | unsigned long rate, | ||
675 | unsigned long parent_rate, | ||
676 | u8 index) | ||
677 | { | ||
678 | int ret; | ||
679 | |||
680 | if (!hw || !rate) | ||
681 | return -EINVAL; | ||
682 | |||
683 | /* | ||
684 | * clk-ref at index[0], in which case we only need to set rate, | ||
685 | * the parent will be changed automatically with the lock sequence. | ||
686 | * With clk-bypass case we only need to change parent. | ||
687 | */ | ||
688 | if (index) | ||
689 | ret = omap3_noncore_dpll_set_parent(hw, index); | ||
690 | else | ||
691 | ret = omap3_noncore_dpll_set_rate_new(hw, rate, parent_rate); | ||
692 | |||
693 | return ret; | ||
694 | } | ||
695 | |||
549 | /* DPLL autoidle read/set code */ | 696 | /* DPLL autoidle read/set code */ |
550 | 697 | ||
551 | /** | 698 | /** |
diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index f75acbf70e96..6f9fb77ffdd5 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h | |||
@@ -254,8 +254,17 @@ extern const struct clk_ops ti_clk_mux_ops; | |||
254 | void omap2_init_clk_hw_omap_clocks(struct clk *clk); | 254 | void omap2_init_clk_hw_omap_clocks(struct clk *clk); |
255 | int omap3_noncore_dpll_enable(struct clk_hw *hw); | 255 | int omap3_noncore_dpll_enable(struct clk_hw *hw); |
256 | void omap3_noncore_dpll_disable(struct clk_hw *hw); | 256 | void omap3_noncore_dpll_disable(struct clk_hw *hw); |
257 | int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index); | ||
257 | int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, | 258 | int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, |
258 | unsigned long parent_rate); | 259 | unsigned long parent_rate); |
260 | int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw, | ||
261 | unsigned long rate, | ||
262 | unsigned long parent_rate, | ||
263 | u8 index); | ||
264 | long omap3_noncore_dpll_determine_rate(struct clk_hw *hw, | ||
265 | unsigned long rate, | ||
266 | unsigned long *best_parent_rate, | ||
267 | struct clk **best_parent_clk); | ||
259 | unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, | 268 | unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, |
260 | unsigned long parent_rate); | 269 | unsigned long parent_rate); |
261 | long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, | 270 | long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, |