diff options
Diffstat (limited to 'arch/arm/mach-omap2/clock34xx.c')
-rw-r--r-- | arch/arm/mach-omap2/clock34xx.c | 299 |
1 files changed, 295 insertions, 4 deletions
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c index b42bdd6079a5..4263099b1ad3 100644 --- a/arch/arm/mach-omap2/clock34xx.c +++ b/arch/arm/mach-omap2/clock34xx.c | |||
@@ -1,10 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * OMAP3-specific clock framework functions | 2 | * OMAP3-specific clock framework functions |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Texas Instruments, Inc. | 4 | * Copyright (C) 2007-2008 Texas Instruments, Inc. |
5 | * Copyright (C) 2007 Nokia Corporation | 5 | * Copyright (C) 2007-2008 Nokia Corporation |
6 | * | 6 | * |
7 | * Written by Paul Walmsley | 7 | * Written by Paul Walmsley |
8 | * Testing and integration fixes by Jouni Högander | ||
8 | * | 9 | * |
9 | * Parts of this code are based on code written by | 10 | * Parts of this code are based on code written by |
10 | * Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu | 11 | * Richard Woodruff, Tony Lindgren, Tuukka Tikkanen, Karthik Dasu |
@@ -23,6 +24,7 @@ | |||
23 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
24 | #include <linux/clk.h> | 25 | #include <linux/clk.h> |
25 | #include <linux/io.h> | 26 | #include <linux/io.h> |
27 | #include <linux/limits.h> | ||
26 | 28 | ||
27 | #include <asm/arch/clock.h> | 29 | #include <asm/arch/clock.h> |
28 | #include <asm/arch/sram.h> | 30 | #include <asm/arch/sram.h> |
@@ -37,8 +39,11 @@ | |||
37 | #include "cm.h" | 39 | #include "cm.h" |
38 | #include "cm-regbits-34xx.h" | 40 | #include "cm-regbits-34xx.h" |
39 | 41 | ||
40 | /* CM_CLKEN_PLL*.EN* bit values */ | 42 | /* CM_AUTOIDLE_PLL*.AUTO_* bit values */ |
41 | #define DPLL_LOCKED 0x7 | 43 | #define DPLL_AUTOIDLE_DISABLE 0x0 |
44 | #define DPLL_AUTOIDLE_LOW_POWER_STOP 0x1 | ||
45 | |||
46 | #define MAX_DPLL_WAIT_TRIES 1000000 | ||
42 | 47 | ||
43 | /** | 48 | /** |
44 | * omap3_dpll_recalc - recalculate DPLL rate | 49 | * omap3_dpll_recalc - recalculate DPLL rate |
@@ -53,6 +58,290 @@ static void omap3_dpll_recalc(struct clk *clk) | |||
53 | propagate_rate(clk); | 58 | propagate_rate(clk); |
54 | } | 59 | } |
55 | 60 | ||
61 | /* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */ | ||
62 | static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits) | ||
63 | { | ||
64 | const struct dpll_data *dd; | ||
65 | |||
66 | dd = clk->dpll_data; | ||
67 | |||
68 | cm_rmw_reg_bits(dd->enable_mask, clken_bits << __ffs(dd->enable_mask), | ||
69 | dd->control_reg); | ||
70 | } | ||
71 | |||
72 | /* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ | ||
73 | static int _omap3_wait_dpll_status(struct clk *clk, u8 state) | ||
74 | { | ||
75 | const struct dpll_data *dd; | ||
76 | int i = 0; | ||
77 | int ret = -EINVAL; | ||
78 | u32 idlest_mask; | ||
79 | |||
80 | dd = clk->dpll_data; | ||
81 | |||
82 | state <<= dd->idlest_bit; | ||
83 | idlest_mask = 1 << dd->idlest_bit; | ||
84 | |||
85 | while (((cm_read_reg(dd->idlest_reg) & idlest_mask) != state) && | ||
86 | i < MAX_DPLL_WAIT_TRIES) { | ||
87 | i++; | ||
88 | udelay(1); | ||
89 | } | ||
90 | |||
91 | if (i == MAX_DPLL_WAIT_TRIES) { | ||
92 | printk(KERN_ERR "clock: %s failed transition to '%s'\n", | ||
93 | clk->name, (state) ? "locked" : "bypassed"); | ||
94 | } else { | ||
95 | pr_debug("clock: %s transition to '%s' in %d loops\n", | ||
96 | clk->name, (state) ? "locked" : "bypassed", i); | ||
97 | |||
98 | ret = 0; | ||
99 | } | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | /* Non-CORE DPLL (e.g., DPLLs that do not control SDRC) clock functions */ | ||
105 | |||
106 | /* | ||
107 | * _omap3_noncore_dpll_lock - instruct a DPLL to lock and wait for readiness | ||
108 | * @clk: pointer to a DPLL struct clk | ||
109 | * | ||
110 | * Instructs a non-CORE DPLL to lock. Waits for the DPLL to report | ||
111 | * readiness before returning. Will save and restore the DPLL's | ||
112 | * autoidle state across the enable, per the CDP code. If the DPLL | ||
113 | * locked successfully, return 0; if the DPLL did not lock in the time | ||
114 | * allotted, or DPLL3 was passed in, return -EINVAL. | ||
115 | */ | ||
116 | static int _omap3_noncore_dpll_lock(struct clk *clk) | ||
117 | { | ||
118 | u8 ai; | ||
119 | int r; | ||
120 | |||
121 | if (clk == &dpll3_ck) | ||
122 | return -EINVAL; | ||
123 | |||
124 | pr_debug("clock: locking DPLL %s\n", clk->name); | ||
125 | |||
126 | ai = omap3_dpll_autoidle_read(clk); | ||
127 | |||
128 | _omap3_dpll_write_clken(clk, DPLL_LOCKED); | ||
129 | |||
130 | if (ai) { | ||
131 | /* | ||
132 | * If no downstream clocks are enabled, CM_IDLEST bit | ||
133 | * may never become active, so don't wait for DPLL to lock. | ||
134 | */ | ||
135 | r = 0; | ||
136 | omap3_dpll_allow_idle(clk); | ||
137 | } else { | ||
138 | r = _omap3_wait_dpll_status(clk, 1); | ||
139 | omap3_dpll_deny_idle(clk); | ||
140 | }; | ||
141 | |||
142 | return r; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness | ||
147 | * @clk: pointer to a DPLL struct clk | ||
148 | * | ||
149 | * Instructs a non-CORE DPLL to enter low-power bypass mode. In | ||
150 | * bypass mode, the DPLL's rate is set equal to its parent clock's | ||
151 | * rate. Waits for the DPLL to report readiness before returning. | ||
152 | * Will save and restore the DPLL's autoidle state across the enable, | ||
153 | * per the CDP code. If the DPLL entered bypass mode successfully, | ||
154 | * return 0; if the DPLL did not enter bypass in the time allotted, or | ||
155 | * DPLL3 was passed in, or the DPLL does not support low-power bypass, | ||
156 | * return -EINVAL. | ||
157 | */ | ||
158 | static int _omap3_noncore_dpll_bypass(struct clk *clk) | ||
159 | { | ||
160 | int r; | ||
161 | u8 ai; | ||
162 | |||
163 | if (clk == &dpll3_ck) | ||
164 | return -EINVAL; | ||
165 | |||
166 | if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) | ||
167 | return -EINVAL; | ||
168 | |||
169 | pr_debug("clock: configuring DPLL %s for low-power bypass\n", | ||
170 | clk->name); | ||
171 | |||
172 | ai = omap3_dpll_autoidle_read(clk); | ||
173 | |||
174 | _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_BYPASS); | ||
175 | |||
176 | r = _omap3_wait_dpll_status(clk, 0); | ||
177 | |||
178 | if (ai) | ||
179 | omap3_dpll_allow_idle(clk); | ||
180 | else | ||
181 | omap3_dpll_deny_idle(clk); | ||
182 | |||
183 | return r; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * _omap3_noncore_dpll_stop - instruct a DPLL to stop | ||
188 | * @clk: pointer to a DPLL struct clk | ||
189 | * | ||
190 | * Instructs a non-CORE DPLL to enter low-power stop. Will save and | ||
191 | * restore the DPLL's autoidle state across the stop, per the CDP | ||
192 | * code. If DPLL3 was passed in, or the DPLL does not support | ||
193 | * low-power stop, return -EINVAL; otherwise, return 0. | ||
194 | */ | ||
195 | static int _omap3_noncore_dpll_stop(struct clk *clk) | ||
196 | { | ||
197 | u8 ai; | ||
198 | |||
199 | if (clk == &dpll3_ck) | ||
200 | return -EINVAL; | ||
201 | |||
202 | if (!(clk->dpll_data->modes & (1 << DPLL_LOW_POWER_STOP))) | ||
203 | return -EINVAL; | ||
204 | |||
205 | pr_debug("clock: stopping DPLL %s\n", clk->name); | ||
206 | |||
207 | ai = omap3_dpll_autoidle_read(clk); | ||
208 | |||
209 | _omap3_dpll_write_clken(clk, DPLL_LOW_POWER_STOP); | ||
210 | |||
211 | if (ai) | ||
212 | omap3_dpll_allow_idle(clk); | ||
213 | else | ||
214 | omap3_dpll_deny_idle(clk); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode | ||
221 | * @clk: pointer to a DPLL struct clk | ||
222 | * | ||
223 | * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. | ||
224 | * The choice of modes depends on the DPLL's programmed rate: if it is | ||
225 | * the same as the DPLL's parent clock, it will enter bypass; | ||
226 | * otherwise, it will enter lock. This code will wait for the DPLL to | ||
227 | * indicate readiness before returning, unless the DPLL takes too long | ||
228 | * to enter the target state. Intended to be used as the struct clk's | ||
229 | * enable function. If DPLL3 was passed in, or the DPLL does not | ||
230 | * support low-power stop, or if the DPLL took too long to enter | ||
231 | * bypass or lock, return -EINVAL; otherwise, return 0. | ||
232 | */ | ||
233 | static int omap3_noncore_dpll_enable(struct clk *clk) | ||
234 | { | ||
235 | int r; | ||
236 | |||
237 | if (clk == &dpll3_ck) | ||
238 | return -EINVAL; | ||
239 | |||
240 | if (clk->parent->rate == clk_get_rate(clk)) | ||
241 | r = _omap3_noncore_dpll_bypass(clk); | ||
242 | else | ||
243 | r = _omap3_noncore_dpll_lock(clk); | ||
244 | |||
245 | return r; | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode | ||
250 | * @clk: pointer to a DPLL struct clk | ||
251 | * | ||
252 | * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. | ||
253 | * The choice of modes depends on the DPLL's programmed rate: if it is | ||
254 | * the same as the DPLL's parent clock, it will enter bypass; | ||
255 | * otherwise, it will enter lock. This code will wait for the DPLL to | ||
256 | * indicate readiness before returning, unless the DPLL takes too long | ||
257 | * to enter the target state. Intended to be used as the struct clk's | ||
258 | * enable function. If DPLL3 was passed in, or the DPLL does not | ||
259 | * support low-power stop, or if the DPLL took too long to enter | ||
260 | * bypass or lock, return -EINVAL; otherwise, return 0. | ||
261 | */ | ||
262 | static void omap3_noncore_dpll_disable(struct clk *clk) | ||
263 | { | ||
264 | if (clk == &dpll3_ck) | ||
265 | return; | ||
266 | |||
267 | _omap3_noncore_dpll_stop(clk); | ||
268 | } | ||
269 | |||
270 | /** | ||
271 | * omap3_dpll_autoidle_read - read a DPLL's autoidle bits | ||
272 | * @clk: struct clk * of the DPLL to read | ||
273 | * | ||
274 | * Return the DPLL's autoidle bits, shifted down to bit 0. Returns | ||
275 | * -EINVAL if passed a null pointer or if the struct clk does not | ||
276 | * appear to refer to a DPLL. | ||
277 | */ | ||
278 | static u32 omap3_dpll_autoidle_read(struct clk *clk) | ||
279 | { | ||
280 | const struct dpll_data *dd; | ||
281 | u32 v; | ||
282 | |||
283 | if (!clk || !clk->dpll_data) | ||
284 | return -EINVAL; | ||
285 | |||
286 | dd = clk->dpll_data; | ||
287 | |||
288 | v = cm_read_reg(dd->autoidle_reg); | ||
289 | v &= dd->autoidle_mask; | ||
290 | v >>= __ffs(dd->autoidle_mask); | ||
291 | |||
292 | return v; | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * omap3_dpll_allow_idle - enable DPLL autoidle bits | ||
297 | * @clk: struct clk * of the DPLL to operate on | ||
298 | * | ||
299 | * Enable DPLL automatic idle control. This automatic idle mode | ||
300 | * switching takes effect only when the DPLL is locked, at least on | ||
301 | * OMAP3430. The DPLL will enter low-power stop when its downstream | ||
302 | * clocks are gated. No return value. | ||
303 | */ | ||
304 | static void omap3_dpll_allow_idle(struct clk *clk) | ||
305 | { | ||
306 | const struct dpll_data *dd; | ||
307 | |||
308 | if (!clk || !clk->dpll_data) | ||
309 | return; | ||
310 | |||
311 | dd = clk->dpll_data; | ||
312 | |||
313 | /* | ||
314 | * REVISIT: CORE DPLL can optionally enter low-power bypass | ||
315 | * by writing 0x5 instead of 0x1. Add some mechanism to | ||
316 | * optionally enter this mode. | ||
317 | */ | ||
318 | cm_rmw_reg_bits(dd->autoidle_mask, | ||
319 | DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask), | ||
320 | dd->autoidle_reg); | ||
321 | } | ||
322 | |||
323 | /** | ||
324 | * omap3_dpll_deny_idle - prevent DPLL from automatically idling | ||
325 | * @clk: struct clk * of the DPLL to operate on | ||
326 | * | ||
327 | * Disable DPLL automatic idle control. No return value. | ||
328 | */ | ||
329 | static void omap3_dpll_deny_idle(struct clk *clk) | ||
330 | { | ||
331 | const struct dpll_data *dd; | ||
332 | |||
333 | if (!clk || !clk->dpll_data) | ||
334 | return; | ||
335 | |||
336 | dd = clk->dpll_data; | ||
337 | |||
338 | cm_rmw_reg_bits(dd->autoidle_mask, | ||
339 | DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask), | ||
340 | dd->autoidle_reg); | ||
341 | } | ||
342 | |||
343 | /* Clock control for DPLL outputs */ | ||
344 | |||
56 | /** | 345 | /** |
57 | * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate | 346 | * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate |
58 | * @clk: DPLL output struct clk | 347 | * @clk: DPLL output struct clk |
@@ -89,6 +378,8 @@ static void omap3_clkoutx2_recalc(struct clk *clk) | |||
89 | propagate_rate(clk); | 378 | propagate_rate(clk); |
90 | } | 379 | } |
91 | 380 | ||
381 | /* Common clock code */ | ||
382 | |||
92 | /* | 383 | /* |
93 | * As it is structured now, this will prevent an OMAP2/3 multiboot | 384 | * As it is structured now, this will prevent an OMAP2/3 multiboot |
94 | * kernel from compiling. This will need further attention. | 385 | * kernel from compiling. This will need further attention. |