aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/clock34xx.c
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2009-02-19 08:29:22 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-02-19 12:28:30 -0500
commitc0bf31320dea2cbcbab1f53ee15a8520f762409b (patch)
tree1b4fbb4396da448eb116f2bee8b58030b63cfe3b /arch/arm/mach-omap2/clock34xx.c
parent8b9dbc16d4f5786c6c930ab028722e3ed7e4285b (diff)
[ARM] omap: add support for bypassing DPLLs
This roughly corresponds with OMAP commits: 7d06c48, 3241b19, 88b5d9b, 18a5500, 9c909ac, 5c6497b, 8b1f0bd, 2ac1da8. For both OMAP2 and OMAP3, we note the reference and bypass clocks in the DPLL data structure. Whenever we modify the DPLL rate, we first ensure that both the reference and bypass clocks are enabled. Then, we decide whether to use the reference and DPLL, or the bypass clock if the desired rate is identical to the bypass rate, and program the DPLL appropriately. Finally, we update the clock's parent, and then disable the unused clocks. This keeps the parents correctly balanced, and more importantly ensures that the bypass clock is running whenever we reprogram the DPLL. This is especially important because the procedure for reprogramming the DPLL involves switching to the bypass clock. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-omap2/clock34xx.c')
-rw-r--r--arch/arm/mach-omap2/clock34xx.c90
1 files changed, 71 insertions, 19 deletions
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index fb0f53b96811..0a14dca31e30 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -93,7 +93,6 @@ static struct omap_clk omap34xx_clks[] = {
93 CLK(NULL, "omap_96m_alwon_fck", &omap_96m_alwon_fck, CK_343X), 93 CLK(NULL, "omap_96m_alwon_fck", &omap_96m_alwon_fck, CK_343X),
94 CLK(NULL, "omap_96m_fck", &omap_96m_fck, CK_343X), 94 CLK(NULL, "omap_96m_fck", &omap_96m_fck, CK_343X),
95 CLK(NULL, "cm_96m_fck", &cm_96m_fck, CK_343X), 95 CLK(NULL, "cm_96m_fck", &cm_96m_fck, CK_343X),
96 CLK(NULL, "virt_omap_54m_fck", &virt_omap_54m_fck, CK_343X),
97 CLK(NULL, "omap_54m_fck", &omap_54m_fck, CK_343X), 96 CLK(NULL, "omap_54m_fck", &omap_54m_fck, CK_343X),
98 CLK(NULL, "omap_48m_fck", &omap_48m_fck, CK_343X), 97 CLK(NULL, "omap_48m_fck", &omap_48m_fck, CK_343X),
99 CLK(NULL, "omap_12m_fck", &omap_12m_fck, CK_343X), 98 CLK(NULL, "omap_12m_fck", &omap_12m_fck, CK_343X),
@@ -110,7 +109,6 @@ static struct omap_clk omap34xx_clks[] = {
110 CLK(NULL, "emu_per_alwon_ck", &emu_per_alwon_ck, CK_343X), 109 CLK(NULL, "emu_per_alwon_ck", &emu_per_alwon_ck, CK_343X),
111 CLK(NULL, "dpll5_ck", &dpll5_ck, CK_3430ES2), 110 CLK(NULL, "dpll5_ck", &dpll5_ck, CK_3430ES2),
112 CLK(NULL, "dpll5_m2_ck", &dpll5_m2_ck, CK_3430ES2), 111 CLK(NULL, "dpll5_m2_ck", &dpll5_m2_ck, CK_3430ES2),
113 CLK(NULL, "omap_120m_fck", &omap_120m_fck, CK_3430ES2),
114 CLK(NULL, "clkout2_src_ck", &clkout2_src_ck, CK_343X), 112 CLK(NULL, "clkout2_src_ck", &clkout2_src_ck, CK_343X),
115 CLK(NULL, "sys_clkout2", &sys_clkout2, CK_343X), 113 CLK(NULL, "sys_clkout2", &sys_clkout2, CK_343X),
116 CLK(NULL, "corex2_fck", &corex2_fck, CK_343X), 114 CLK(NULL, "corex2_fck", &corex2_fck, CK_343X),
@@ -344,7 +342,7 @@ static u16 _omap3_dpll_compute_freqsel(struct clk *clk, u8 n)
344 unsigned long fint; 342 unsigned long fint;
345 u16 f = 0; 343 u16 f = 0;
346 344
347 fint = clk->parent->rate / (n + 1); 345 fint = clk->dpll_data->clk_ref->rate / (n + 1);
348 346
349 pr_debug("clock: fint is %lu\n", fint); 347 pr_debug("clock: fint is %lu\n", fint);
350 348
@@ -411,7 +409,7 @@ static int _omap3_noncore_dpll_lock(struct clk *clk)
411} 409}
412 410
413/* 411/*
414 * omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness 412 * _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness
415 * @clk: pointer to a DPLL struct clk 413 * @clk: pointer to a DPLL struct clk
416 * 414 *
417 * Instructs a non-CORE DPLL to enter low-power bypass mode. In 415 * Instructs a non-CORE DPLL to enter low-power bypass mode. In
@@ -501,14 +499,25 @@ static int _omap3_noncore_dpll_stop(struct clk *clk)
501static int omap3_noncore_dpll_enable(struct clk *clk) 499static int omap3_noncore_dpll_enable(struct clk *clk)
502{ 500{
503 int r; 501 int r;
502 struct dpll_data *dd;
504 503
505 if (clk == &dpll3_ck) 504 if (clk == &dpll3_ck)
506 return -EINVAL; 505 return -EINVAL;
507 506
508 if (clk->parent->rate == omap2_get_dpll_rate(clk)) 507 dd = clk->dpll_data;
508 if (!dd)
509 return -EINVAL;
510
511 if (clk->rate == dd->clk_bypass->rate) {
512 WARN_ON(clk->parent != dd->clk_bypass);
509 r = _omap3_noncore_dpll_bypass(clk); 513 r = _omap3_noncore_dpll_bypass(clk);
510 else 514 } else {
515 WARN_ON(clk->parent != dd->clk_ref);
511 r = _omap3_noncore_dpll_lock(clk); 516 r = _omap3_noncore_dpll_lock(clk);
517 }
518 /* FIXME: this is dubious - if clk->rate has changed, what about propagating? */
519 if (!r)
520 clk->rate = omap2_get_dpll_rate(clk);
512 521
513 return r; 522 return r;
514} 523}
@@ -583,13 +592,18 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
583 * @clk: struct clk * of DPLL to set 592 * @clk: struct clk * of DPLL to set
584 * @rate: rounded target rate 593 * @rate: rounded target rate
585 * 594 *
586 * Program the DPLL with the rounded target rate. Returns -EINVAL upon 595 * Set the DPLL CLKOUT to the target rate. If the DPLL can enter
587 * error, or 0 upon success. 596 * low-power bypass, and the target rate is the bypass source clock
597 * rate, then configure the DPLL for bypass. Otherwise, round the
598 * target rate if it hasn't been done already, then program and lock
599 * the DPLL. Returns -EINVAL upon error, or 0 upon success.
588 */ 600 */
589static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) 601static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
590{ 602{
603 struct clk *new_parent = NULL;
591 u16 freqsel; 604 u16 freqsel;
592 struct dpll_data *dd; 605 struct dpll_data *dd;
606 int ret;
593 607
594 if (!clk || !rate) 608 if (!clk || !rate)
595 return -EINVAL; 609 return -EINVAL;
@@ -601,18 +615,56 @@ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
601 if (rate == omap2_get_dpll_rate(clk)) 615 if (rate == omap2_get_dpll_rate(clk))
602 return 0; 616 return 0;
603 617
604 if (dd->last_rounded_rate != rate) 618 /*
605 omap2_dpll_round_rate(clk, rate); 619 * Ensure both the bypass and ref clocks are enabled prior to
620 * doing anything; we need the bypass clock running to reprogram
621 * the DPLL.
622 */
623 omap2_clk_enable(dd->clk_bypass);
624 omap2_clk_enable(dd->clk_ref);
625
626 if (dd->clk_bypass->rate == rate &&
627 (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
628 pr_debug("clock: %s: set rate: entering bypass.\n", clk->name);
606 629
607 if (dd->last_rounded_rate == 0) 630 ret = _omap3_noncore_dpll_bypass(clk);
608 return -EINVAL; 631 if (!ret)
632 new_parent = dd->clk_bypass;
633 } else {
634 if (dd->last_rounded_rate != rate)
635 omap2_dpll_round_rate(clk, rate);
636
637 if (dd->last_rounded_rate == 0)
638 return -EINVAL;
639
640 freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n);
641 if (!freqsel)
642 WARN_ON(1);
609 643
610 freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); 644 pr_debug("clock: %s: set rate: locking rate to %lu.\n",
611 if (!freqsel) 645 clk->name, rate);
612 WARN_ON(1);
613 646
614 omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, 647 ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
615 freqsel); 648 dd->last_rounded_n, freqsel);
649 if (!ret)
650 new_parent = dd->clk_ref;
651 }
652 if (!ret) {
653 /*
654 * Switch the parent clock in the heirarchy, and make sure
655 * that the new parent's usecount is correct. Note: we
656 * enable the new parent before disabling the old to avoid
657 * any unnecessary hardware disable->enable transitions.
658 */
659 if (clk->usecount) {
660 omap2_clk_enable(new_parent);
661 omap2_clk_disable(clk->parent);
662 }
663 clk_reparent(clk, new_parent);
664 clk->rate = rate;
665 }
666 omap2_clk_disable(dd->clk_ref);
667 omap2_clk_disable(dd->clk_bypass);
616 668
617 return 0; 669 return 0;
618} 670}
@@ -804,11 +856,11 @@ static unsigned long omap3_clkoutx2_recalc(struct clk *clk)
804 856
805 dd = pclk->dpll_data; 857 dd = pclk->dpll_data;
806 858
807 WARN_ON(!dd->control_reg || !dd->enable_mask); 859 WARN_ON(!dd->enable_mask);
808 860
809 v = __raw_readl(dd->control_reg) & dd->enable_mask; 861 v = __raw_readl(dd->control_reg) & dd->enable_mask;
810 v >>= __ffs(dd->enable_mask); 862 v >>= __ffs(dd->enable_mask);
811 if (v != DPLL_LOCKED) 863 if (v != OMAP3XXX_EN_DPLL_LOCKED)
812 rate = clk->parent->rate; 864 rate = clk->parent->rate;
813 else 865 else
814 rate = clk->parent->rate * 2; 866 rate = clk->parent->rate * 2;