aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2014-01-30 06:17:20 -0500
committerPaul Walmsley <paul@pwsan.com>2014-02-19 14:07:55 -0500
commit994c41ee0ac875797b4dfef509ac7753e2649b4d (patch)
tree64b613ac1a3397891f452af06e6c4ebe09a9905a /arch/arm
parent01142519ffc0734436f26b01aeed37a915dece05 (diff)
ARM: OMAP2+: clock: fix clkoutx2 with CLK_SET_RATE_PARENT
If CLK_SET_RATE_PARENT is set for a clkoutx2 clock, calling clk_set_rate() on the clock "skips" the x2 multiplier as there are no set_rate and round_rate functions defined for the clkoutx2. This results in getting double the requested clock rates, breaking the display on omap3430 based devices. This got broken when d0f58bd3bba3877fb1af4664c4e33273d36f00e4 and related patches were merged for v3.14, as omapdss driver now relies more on the clk-framework and CLK_SET_RATE_PARENT. This patch implements set_rate and round_rate for clkoutx2. Tested on OMAP3430, OMAP3630, OMAP4460. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Acked-by: Tero Kristo <t-kristo@ti.com> Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-omap2/cclock3xxx_data.c2
-rw-r--r--arch/arm/mach-omap2/dpll3xxx.c92
2 files changed, 79 insertions, 15 deletions
diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c
index 3b05aea56d1f..11ed9152e665 100644
--- a/arch/arm/mach-omap2/cclock3xxx_data.c
+++ b/arch/arm/mach-omap2/cclock3xxx_data.c
@@ -433,7 +433,9 @@ static const struct clk_ops dpll4_m5x2_ck_ops = {
433 .enable = &omap2_dflt_clk_enable, 433 .enable = &omap2_dflt_clk_enable,
434 .disable = &omap2_dflt_clk_disable, 434 .disable = &omap2_dflt_clk_disable,
435 .is_enabled = &omap2_dflt_clk_is_enabled, 435 .is_enabled = &omap2_dflt_clk_is_enabled,
436 .set_rate = &omap3_clkoutx2_set_rate,
436 .recalc_rate = &omap3_clkoutx2_recalc, 437 .recalc_rate = &omap3_clkoutx2_recalc,
438 .round_rate = &omap3_clkoutx2_round_rate,
437}; 439};
438 440
439static const struct clk_ops dpll4_m5x2_ck_3630_ops = { 441static const struct clk_ops dpll4_m5x2_ck_3630_ops = {
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c
index 3185ced807c9..3c418ea54bbe 100644
--- a/arch/arm/mach-omap2/dpll3xxx.c
+++ b/arch/arm/mach-omap2/dpll3xxx.c
@@ -623,6 +623,32 @@ void omap3_dpll_deny_idle(struct clk_hw_omap *clk)
623 623
624/* Clock control for DPLL outputs */ 624/* Clock control for DPLL outputs */
625 625
626/* Find the parent DPLL for the given clkoutx2 clock */
627static struct clk_hw_omap *omap3_find_clkoutx2_dpll(struct clk_hw *hw)
628{
629 struct clk_hw_omap *pclk = NULL;
630 struct clk *parent;
631
632 /* Walk up the parents of clk, looking for a DPLL */
633 do {
634 do {
635 parent = __clk_get_parent(hw->clk);
636 hw = __clk_get_hw(parent);
637 } while (hw && (__clk_get_flags(hw->clk) & CLK_IS_BASIC));
638 if (!hw)
639 break;
640 pclk = to_clk_hw_omap(hw);
641 } while (pclk && !pclk->dpll_data);
642
643 /* clk does not have a DPLL as a parent? error in the clock data */
644 if (!pclk) {
645 WARN_ON(1);
646 return NULL;
647 }
648
649 return pclk;
650}
651
626/** 652/**
627 * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate 653 * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate
628 * @clk: DPLL output struct clk 654 * @clk: DPLL output struct clk
@@ -637,27 +663,14 @@ unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
637 unsigned long rate; 663 unsigned long rate;
638 u32 v; 664 u32 v;
639 struct clk_hw_omap *pclk = NULL; 665 struct clk_hw_omap *pclk = NULL;
640 struct clk *parent;
641 666
642 if (!parent_rate) 667 if (!parent_rate)
643 return 0; 668 return 0;
644 669
645 /* Walk up the parents of clk, looking for a DPLL */ 670 pclk = omap3_find_clkoutx2_dpll(hw);
646 do {
647 do {
648 parent = __clk_get_parent(hw->clk);
649 hw = __clk_get_hw(parent);
650 } while (hw && (__clk_get_flags(hw->clk) & CLK_IS_BASIC));
651 if (!hw)
652 break;
653 pclk = to_clk_hw_omap(hw);
654 } while (pclk && !pclk->dpll_data);
655 671
656 /* clk does not have a DPLL as a parent? error in the clock data */ 672 if (!pclk)
657 if (!pclk) {
658 WARN_ON(1);
659 return 0; 673 return 0;
660 }
661 674
662 dd = pclk->dpll_data; 675 dd = pclk->dpll_data;
663 676
@@ -672,6 +685,55 @@ unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
672 return rate; 685 return rate;
673} 686}
674 687
688int omap3_clkoutx2_set_rate(struct clk_hw *hw, unsigned long rate,
689 unsigned long parent_rate)
690{
691 return 0;
692}
693
694long omap3_clkoutx2_round_rate(struct clk_hw *hw, unsigned long rate,
695 unsigned long *prate)
696{
697 const struct dpll_data *dd;
698 u32 v;
699 struct clk_hw_omap *pclk = NULL;
700
701 if (!*prate)
702 return 0;
703
704 pclk = omap3_find_clkoutx2_dpll(hw);
705
706 if (!pclk)
707 return 0;
708
709 dd = pclk->dpll_data;
710
711 /* TYPE J does not have a clkoutx2 */
712 if (dd->flags & DPLL_J_TYPE) {
713 *prate = __clk_round_rate(__clk_get_parent(pclk->hw.clk), rate);
714 return *prate;
715 }
716
717 WARN_ON(!dd->enable_mask);
718
719 v = omap2_clk_readl(pclk, dd->control_reg) & dd->enable_mask;
720 v >>= __ffs(dd->enable_mask);
721
722 /* If in bypass, the rate is fixed to the bypass rate*/
723 if (v != OMAP3XXX_EN_DPLL_LOCKED)
724 return *prate;
725
726 if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) {
727 unsigned long best_parent;
728
729 best_parent = (rate / 2);
730 *prate = __clk_round_rate(__clk_get_parent(hw->clk),
731 best_parent);
732 }
733
734 return *prate * 2;
735}
736
675/* OMAP3/4 non-CORE DPLL clkops */ 737/* OMAP3/4 non-CORE DPLL clkops */
676const struct clk_hw_omap_ops clkhwops_omap3_dpll = { 738const struct clk_hw_omap_ops clkhwops_omap3_dpll = {
677 .allow_idle = omap3_dpll_allow_idle, 739 .allow_idle = omap3_dpll_allow_idle,