aboutsummaryrefslogtreecommitdiffstats
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 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/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 5020cb1f2e7e..40cb65ba1fac 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)
211 return; 211 return;
212} 212}
213 213
214/* Returns the DPLL rate */ 214/**
215 * omap2_get_dpll_rate - returns the current DPLL CLKOUT rate
216 * @clk: struct clk * of a DPLL
217 *
218 * DPLLs can be locked or bypassed - basically, enabled or disabled.
219 * When locked, the DPLL output depends on the M and N values. When
220 * bypassed, on OMAP2xxx, the output rate is either the 32KiHz clock
221 * or sys_clk. Bypass rates on OMAP3 depend on the DPLL: DPLLs 1 and
222 * 2 are bypassed with dpll1_fclk and dpll2_fclk respectively
223 * (generated by DPLL3), while DPLL 3, 4, and 5 bypass rates are sys_clk.
224 * Returns the current DPLL CLKOUT rate (*not* CLKOUTX2) if the DPLL is
225 * locked, or the appropriate bypass rate if the DPLL is bypassed, or 0
226 * if the clock @clk is not a DPLL.
227 */
215u32 omap2_get_dpll_rate(struct clk *clk) 228u32 omap2_get_dpll_rate(struct clk *clk)
216{ 229{
217 long long dpll_clk; 230 long long dpll_clk;
218 u32 dpll_mult, dpll_div, dpll; 231 u32 dpll_mult, dpll_div, v;
219 struct dpll_data *dd; 232 struct dpll_data *dd;
220 233
221 dd = clk->dpll_data; 234 dd = clk->dpll_data;
222 /* REVISIT: What do we return on error? */
223 if (!dd) 235 if (!dd)
224 return 0; 236 return 0;
225 237
226 dpll = __raw_readl(dd->mult_div1_reg); 238 /* Return bypass rate if DPLL is bypassed */
227 dpll_mult = dpll & dd->mult_mask; 239 v = __raw_readl(dd->control_reg);
240 v &= dd->enable_mask;
241 v >>= __ffs(dd->enable_mask);
242
243 if (cpu_is_omap24xx()) {
244 if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
245 v == OMAP2XXX_EN_DPLL_FRBYPASS)
246 return dd->clk_bypass->rate;
247 } else if (cpu_is_omap34xx()) {
248 if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
249 v == OMAP3XXX_EN_DPLL_FRBYPASS)
250 return dd->clk_bypass->rate;
251 }
252
253 v = __raw_readl(dd->mult_div1_reg);
254 dpll_mult = v & dd->mult_mask;
228 dpll_mult >>= __ffs(dd->mult_mask); 255 dpll_mult >>= __ffs(dd->mult_mask);
229 dpll_div = dpll & dd->div1_mask; 256 dpll_div = v & dd->div1_mask;
230 dpll_div >>= __ffs(dd->div1_mask); 257 dpll_div >>= __ffs(dd->div1_mask);
231 258
232 dpll_clk = (long long)clk->parent->rate * dpll_mult; 259 dpll_clk = (long long)dd->clk_ref->rate * dpll_mult;
233 do_div(dpll_clk, dpll_div + 1); 260 do_div(dpll_clk, dpll_div + 1);
234 261
235 return dpll_clk; 262 return dpll_clk;
@@ -930,7 +957,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
930 pr_debug("clock: starting DPLL round_rate for clock %s, target rate " 957 pr_debug("clock: starting DPLL round_rate for clock %s, target rate "
931 "%ld\n", clk->name, target_rate); 958 "%ld\n", clk->name, target_rate);
932 959
933 scaled_rt_rp = target_rate / (clk->parent->rate / DPLL_SCALE_FACTOR); 960 scaled_rt_rp = target_rate / (dd->clk_ref->rate / DPLL_SCALE_FACTOR);
934 scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR; 961 scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR;
935 962
936 dd->last_rounded_rate = 0; 963 dd->last_rounded_rate = 0;
@@ -957,7 +984,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
957 break; 984 break;
958 985
959 r = _dpll_test_mult(&m, n, &new_rate, target_rate, 986 r = _dpll_test_mult(&m, n, &new_rate, target_rate,
960 clk->parent->rate); 987 dd->clk_ref->rate);
961 988
962 /* m can't be set low enough for this n - try with a larger n */ 989 /* m can't be set low enough for this n - try with a larger n */
963 if (r == DPLL_MULT_UNDERFLOW) 990 if (r == DPLL_MULT_UNDERFLOW)
@@ -988,7 +1015,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
988 1015
989 dd->last_rounded_m = min_e_m; 1016 dd->last_rounded_m = min_e_m;
990 dd->last_rounded_n = min_e_n; 1017 dd->last_rounded_n = min_e_n;
991 dd->last_rounded_rate = _dpll_compute_new_rate(clk->parent->rate, 1018 dd->last_rounded_rate = _dpll_compute_new_rate(dd->clk_ref->rate,
992 min_e_m, min_e_n); 1019 min_e_m, min_e_n);
993 1020
994 pr_debug("clock: final least error: e = %d, m = %d, n = %d\n", 1021 pr_debug("clock: final least error: e = %d, m = %d, n = %d\n",