aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2/clock.c
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2009-02-19 13:29:22 +0000
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-02-19 17:28:30 +0000
commitc0bf31320dea2cbcbab1f53ee15a8520f762409b (patch)
tree1b4fbb4396da448eb116f2bee8b58030b63cfe3b /arch/arm/mach-omap2/clock.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/clock.c')
-rw-r--r--arch/arm/mach-omap2/clock.c47
1 files changed, 37 insertions, 10 deletions
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 5020cb1f2e7..40cb65ba1fa 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -211,25 +211,52 @@ void omap2_init_clksel_parent(struct clk *clk)
return;
}
-/* Returns the DPLL rate */
+/**
+ * omap2_get_dpll_rate - returns the current DPLL CLKOUT rate
+ * @clk: struct clk * of a DPLL
+ *
+ * DPLLs can be locked or bypassed - basically, enabled or disabled.
+ * When locked, the DPLL output depends on the M and N values. When
+ * bypassed, on OMAP2xxx, the output rate is either the 32KiHz clock
+ * or sys_clk. Bypass rates on OMAP3 depend on the DPLL: DPLLs 1 and
+ * 2 are bypassed with dpll1_fclk and dpll2_fclk respectively
+ * (generated by DPLL3), while DPLL 3, 4, and 5 bypass rates are sys_clk.
+ * Returns the current DPLL CLKOUT rate (*not* CLKOUTX2) if the DPLL is
+ * locked, or the appropriate bypass rate if the DPLL is bypassed, or 0
+ * if the clock @clk is not a DPLL.
+ */
u32 omap2_get_dpll_rate(struct clk *clk)
{
long long dpll_clk;
- u32 dpll_mult, dpll_div, dpll;
+ u32 dpll_mult, dpll_div, v;
struct dpll_data *dd;
dd = clk->dpll_data;
- /* REVISIT: What do we return on error? */
if (!dd)
return 0;
- dpll = __raw_readl(dd->mult_div1_reg);
- dpll_mult = dpll & dd->mult_mask;
+ /* Return bypass rate if DPLL is bypassed */
+ v = __raw_readl(dd->control_reg);
+ v &= dd->enable_mask;
+ v >>= __ffs(dd->enable_mask);
+
+ if (cpu_is_omap24xx()) {
+ if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
+ v == OMAP2XXX_EN_DPLL_FRBYPASS)
+ return dd->clk_bypass->rate;
+ } else if (cpu_is_omap34xx()) {
+ if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
+ v == OMAP3XXX_EN_DPLL_FRBYPASS)
+ return dd->clk_bypass->rate;
+ }
+
+ v = __raw_readl(dd->mult_div1_reg);
+ dpll_mult = v & dd->mult_mask;
dpll_mult >>= __ffs(dd->mult_mask);
- dpll_div = dpll & dd->div1_mask;
+ dpll_div = v & dd->div1_mask;
dpll_div >>= __ffs(dd->div1_mask);
- dpll_clk = (long long)clk->parent->rate * dpll_mult;
+ dpll_clk = (long long)dd->clk_ref->rate * dpll_mult;
do_div(dpll_clk, dpll_div + 1);
return dpll_clk;
@@ -930,7 +957,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
pr_debug("clock: starting DPLL round_rate for clock %s, target rate "
"%ld\n", clk->name, target_rate);
- scaled_rt_rp = target_rate / (clk->parent->rate / DPLL_SCALE_FACTOR);
+ scaled_rt_rp = target_rate / (dd->clk_ref->rate / DPLL_SCALE_FACTOR);
scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR;
dd->last_rounded_rate = 0;
@@ -957,7 +984,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
break;
r = _dpll_test_mult(&m, n, &new_rate, target_rate,
- clk->parent->rate);
+ dd->clk_ref->rate);
/* m can't be set low enough for this n - try with a larger n */
if (r == DPLL_MULT_UNDERFLOW)
@@ -988,7 +1015,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
dd->last_rounded_m = min_e_m;
dd->last_rounded_n = min_e_n;
- dd->last_rounded_rate = _dpll_compute_new_rate(clk->parent->rate,
+ dd->last_rounded_rate = _dpll_compute_new_rate(dd->clk_ref->rate,
min_e_m, min_e_n);
pr_debug("clock: final least error: e = %d, m = %d, n = %d\n",