aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTero Kristo <t-kristo@ti.com>2014-10-03 09:57:11 -0400
committerPaul Walmsley <paul@pwsan.com>2014-11-13 11:26:16 -0500
commitd539efa37f1f789339699c941e72e320d12d5f28 (patch)
tree8256b57b68bd582e1d8440be29cb6bb74d53abc2
parentf0d2f68a6387867c4748bc94c9b0165a49701204 (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.c147
-rw-r--r--include/linux/clk/ti.h9
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 */
562long 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 */
597int 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 */
624static 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 */
673int 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;
254void omap2_init_clk_hw_omap_clocks(struct clk *clk); 254void omap2_init_clk_hw_omap_clocks(struct clk *clk);
255int omap3_noncore_dpll_enable(struct clk_hw *hw); 255int omap3_noncore_dpll_enable(struct clk_hw *hw);
256void omap3_noncore_dpll_disable(struct clk_hw *hw); 256void omap3_noncore_dpll_disable(struct clk_hw *hw);
257int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index);
257int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, 258int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
258 unsigned long parent_rate); 259 unsigned long parent_rate);
260int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw,
261 unsigned long rate,
262 unsigned long parent_rate,
263 u8 index);
264long 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);
259unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, 268unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
260 unsigned long parent_rate); 269 unsigned long parent_rate);
261long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, 270long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,