diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2009-02-19 08:29:22 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-02-19 12:28:30 -0500 |
commit | c0bf31320dea2cbcbab1f53ee15a8520f762409b (patch) | |
tree | 1b4fbb4396da448eb116f2bee8b58030b63cfe3b /arch/arm/mach-omap2/clock.c | |
parent | 8b9dbc16d4f5786c6c930ab028722e3ed7e4285b (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.c | 47 |
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 | */ | ||
215 | u32 omap2_get_dpll_rate(struct clk *clk) | 228 | u32 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", |