]>
Commit | Line | Data |
---|---|---|
046d6b28 TL |
1 | /* |
2 | * linux/arch/arm/mach-omap2/clock.c | |
3 | * | |
4 | * Copyright (C) 2005 Texas Instruments Inc. | |
5 | * Richard Woodruff <r-woodruff2@ti.com> | |
6 | * Created for OMAP2. | |
7 | * | |
8 | * Cleaned up and modified to use omap shared clock framework by | |
9 | * Tony Lindgren <tony@atomide.com> | |
10 | * | |
11 | * Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation | |
12 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License version 2 as | |
16 | * published by the Free Software Foundation. | |
17 | */ | |
18 | #include <linux/config.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/device.h> | |
22 | #include <linux/list.h> | |
23 | #include <linux/errno.h> | |
24 | #include <linux/delay.h> | |
f8ce2547 | 25 | #include <linux/clk.h> |
046d6b28 TL |
26 | |
27 | #include <asm/io.h> | |
28 | ||
046d6b28 TL |
29 | #include <asm/arch/clock.h> |
30 | #include <asm/arch/sram.h> | |
31 | #include <asm/arch/prcm.h> | |
32 | ||
33 | #include "clock.h" | |
34 | ||
35 | //#define DOWN_VARIABLE_DPLL 1 /* Experimental */ | |
36 | ||
37 | static struct prcm_config *curr_prcm_set; | |
38 | static struct memory_timings mem_timings; | |
39 | static u32 curr_perf_level = PRCM_FULL_SPEED; | |
40 | ||
41 | /*------------------------------------------------------------------------- | |
42 | * Omap2 specific clock functions | |
43 | *-------------------------------------------------------------------------*/ | |
44 | ||
45 | /* Recalculate SYST_CLK */ | |
46 | static void omap2_sys_clk_recalc(struct clk * clk) | |
47 | { | |
48 | u32 div = PRCM_CLKSRC_CTRL; | |
49 | div &= (1 << 7) | (1 << 6); /* Test if ext clk divided by 1 or 2 */ | |
50 | div >>= clk->rate_offset; | |
51 | clk->rate = (clk->parent->rate / div); | |
52 | propagate_rate(clk); | |
53 | } | |
54 | ||
55 | static u32 omap2_get_dpll_rate(struct clk * tclk) | |
56 | { | |
57 | int dpll_clk, dpll_mult, dpll_div, amult; | |
58 | ||
59 | dpll_mult = (CM_CLKSEL1_PLL >> 12) & 0x03ff; /* 10 bits */ | |
60 | dpll_div = (CM_CLKSEL1_PLL >> 8) & 0x0f; /* 4 bits */ | |
61 | dpll_clk = (tclk->parent->rate * dpll_mult) / (dpll_div + 1); | |
62 | amult = CM_CLKSEL2_PLL & 0x3; | |
63 | dpll_clk *= amult; | |
64 | ||
65 | return dpll_clk; | |
66 | } | |
67 | ||
68 | static void omap2_followparent_recalc(struct clk *clk) | |
69 | { | |
70 | followparent_recalc(clk); | |
71 | } | |
72 | ||
73 | static void omap2_propagate_rate(struct clk * clk) | |
74 | { | |
75 | if (!(clk->flags & RATE_FIXED)) | |
76 | clk->rate = clk->parent->rate; | |
77 | ||
78 | propagate_rate(clk); | |
79 | } | |
80 | ||
81 | /* Enable an APLL if off */ | |
82 | static void omap2_clk_fixed_enable(struct clk *clk) | |
83 | { | |
84 | u32 cval, i=0; | |
85 | ||
86 | if (clk->enable_bit == 0xff) /* Parent will do it */ | |
87 | return; | |
88 | ||
89 | cval = CM_CLKEN_PLL; | |
90 | ||
91 | if ((cval & (0x3 << clk->enable_bit)) == (0x3 << clk->enable_bit)) | |
92 | return; | |
93 | ||
94 | cval &= ~(0x3 << clk->enable_bit); | |
95 | cval |= (0x3 << clk->enable_bit); | |
96 | CM_CLKEN_PLL = cval; | |
97 | ||
98 | if (clk == &apll96_ck) | |
99 | cval = (1 << 8); | |
100 | else if (clk == &apll54_ck) | |
101 | cval = (1 << 6); | |
102 | ||
103 | while (!CM_IDLEST_CKGEN & cval) { /* Wait for lock */ | |
104 | ++i; | |
105 | udelay(1); | |
106 | if (i == 100000) | |
107 | break; | |
108 | } | |
109 | } | |
110 | ||
111 | /* Enables clock without considering parent dependencies or use count | |
112 | * REVISIT: Maybe change this to use clk->enable like on omap1? | |
113 | */ | |
114 | static int omap2_clk_enable(struct clk * clk) | |
115 | { | |
116 | u32 regval32; | |
117 | ||
118 | if (clk->flags & ALWAYS_ENABLED) | |
119 | return 0; | |
120 | ||
121 | if (unlikely(clk->enable_reg == 0)) { | |
122 | printk(KERN_ERR "clock.c: Enable for %s without enable code\n", | |
123 | clk->name); | |
124 | return 0; | |
125 | } | |
126 | ||
127 | if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) { | |
128 | omap2_clk_fixed_enable(clk); | |
129 | return 0; | |
130 | } | |
131 | ||
132 | regval32 = __raw_readl(clk->enable_reg); | |
133 | regval32 |= (1 << clk->enable_bit); | |
134 | __raw_writel(regval32, clk->enable_reg); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | /* Stop APLL */ | |
140 | static void omap2_clk_fixed_disable(struct clk *clk) | |
141 | { | |
142 | u32 cval; | |
143 | ||
144 | if(clk->enable_bit == 0xff) /* let parent off do it */ | |
145 | return; | |
146 | ||
147 | cval = CM_CLKEN_PLL; | |
148 | cval &= ~(0x3 << clk->enable_bit); | |
149 | CM_CLKEN_PLL = cval; | |
150 | } | |
151 | ||
152 | /* Disables clock without considering parent dependencies or use count */ | |
153 | static void omap2_clk_disable(struct clk *clk) | |
154 | { | |
155 | u32 regval32; | |
156 | ||
157 | if (clk->enable_reg == 0) | |
158 | return; | |
159 | ||
160 | if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) { | |
161 | omap2_clk_fixed_disable(clk); | |
162 | return; | |
163 | } | |
164 | ||
165 | regval32 = __raw_readl(clk->enable_reg); | |
166 | regval32 &= ~(1 << clk->enable_bit); | |
167 | __raw_writel(regval32, clk->enable_reg); | |
168 | } | |
169 | ||
170 | static int omap2_clk_use(struct clk *clk) | |
171 | { | |
172 | int ret = 0; | |
173 | ||
174 | if (clk->usecount++ == 0) { | |
175 | if (likely((u32)clk->parent)) | |
176 | ret = omap2_clk_use(clk->parent); | |
177 | ||
178 | if (unlikely(ret != 0)) { | |
179 | clk->usecount--; | |
180 | return ret; | |
181 | } | |
182 | ||
183 | ret = omap2_clk_enable(clk); | |
184 | ||
185 | if (unlikely(ret != 0) && clk->parent) { | |
186 | omap2_clk_unuse(clk->parent); | |
187 | clk->usecount--; | |
188 | } | |
189 | } | |
190 | ||
191 | return ret; | |
192 | } | |
193 | ||
194 | static void omap2_clk_unuse(struct clk *clk) | |
195 | { | |
196 | if (clk->usecount > 0 && !(--clk->usecount)) { | |
197 | omap2_clk_disable(clk); | |
198 | if (likely((u32)clk->parent)) | |
199 | omap2_clk_unuse(clk->parent); | |
200 | } | |
201 | } | |
202 | ||
203 | /* | |
204 | * Uses the current prcm set to tell if a rate is valid. | |
205 | * You can go slower, but not faster within a given rate set. | |
206 | */ | |
207 | static u32 omap2_dpll_round_rate(unsigned long target_rate) | |
208 | { | |
209 | u32 high, low; | |
210 | ||
211 | if ((CM_CLKSEL2_PLL & 0x3) == 1) { /* DPLL clockout */ | |
212 | high = curr_prcm_set->dpll_speed * 2; | |
213 | low = curr_prcm_set->dpll_speed; | |
214 | } else { /* DPLL clockout x 2 */ | |
215 | high = curr_prcm_set->dpll_speed; | |
216 | low = curr_prcm_set->dpll_speed / 2; | |
217 | } | |
218 | ||
219 | #ifdef DOWN_VARIABLE_DPLL | |
220 | if (target_rate > high) | |
221 | return high; | |
222 | else | |
223 | return target_rate; | |
224 | #else | |
225 | if (target_rate > low) | |
226 | return high; | |
227 | else | |
228 | return low; | |
229 | #endif | |
230 | ||
231 | } | |
232 | ||
233 | /* | |
234 | * Used for clocks that are part of CLKSEL_xyz governed clocks. | |
235 | * REVISIT: Maybe change to use clk->enable() functions like on omap1? | |
236 | */ | |
237 | static void omap2_clksel_recalc(struct clk * clk) | |
238 | { | |
239 | u32 fixed = 0, div = 0; | |
240 | ||
241 | if (clk == &dpll_ck) { | |
242 | clk->rate = omap2_get_dpll_rate(clk); | |
243 | fixed = 1; | |
244 | div = 0; | |
245 | } | |
246 | ||
247 | if (clk == &iva1_mpu_int_ifck) { | |
248 | div = 2; | |
249 | fixed = 1; | |
250 | } | |
251 | ||
252 | if ((clk == &dss1_fck) && ((CM_CLKSEL1_CORE & (0x1f << 8)) == 0)) { | |
253 | clk->rate = sys_ck.rate; | |
254 | return; | |
255 | } | |
256 | ||
257 | if (!fixed) { | |
258 | div = omap2_clksel_get_divisor(clk); | |
259 | if (div == 0) | |
260 | return; | |
261 | } | |
262 | ||
263 | if (div != 0) { | |
264 | if (unlikely(clk->rate == clk->parent->rate / div)) | |
265 | return; | |
266 | clk->rate = clk->parent->rate / div; | |
267 | } | |
268 | ||
269 | if (unlikely(clk->flags & RATE_PROPAGATES)) | |
270 | propagate_rate(clk); | |
271 | } | |
272 | ||
273 | /* | |
274 | * Finds best divider value in an array based on the source and target | |
275 | * rates. The divider array must be sorted with smallest divider first. | |
276 | */ | |
277 | static inline u32 omap2_divider_from_table(u32 size, u32 *div_array, | |
278 | u32 src_rate, u32 tgt_rate) | |
279 | { | |
280 | int i, test_rate; | |
281 | ||
282 | if (div_array == NULL) | |
283 | return ~1; | |
284 | ||
285 | for (i=0; i < size; i++) { | |
286 | test_rate = src_rate / *div_array; | |
287 | if (test_rate <= tgt_rate) | |
288 | return *div_array; | |
289 | ++div_array; | |
290 | } | |
291 | ||
292 | return ~0; /* No acceptable divider */ | |
293 | } | |
294 | ||
295 | /* | |
296 | * Find divisor for the given clock and target rate. | |
297 | * | |
298 | * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, | |
299 | * they are only settable as part of virtual_prcm set. | |
300 | */ | |
301 | static u32 omap2_clksel_round_rate(struct clk *tclk, u32 target_rate, | |
302 | u32 *new_div) | |
303 | { | |
304 | u32 gfx_div[] = {2, 3, 4}; | |
305 | u32 sysclkout_div[] = {1, 2, 4, 8, 16}; | |
306 | u32 dss1_div[] = {1, 2, 3, 4, 5, 6, 8, 9, 12, 16}; | |
307 | u32 vylnq_div[] = {1, 2, 3, 4, 6, 8, 9, 12, 16, 18}; | |
308 | u32 best_div = ~0, asize = 0; | |
309 | u32 *div_array = NULL; | |
310 | ||
311 | switch (tclk->flags & SRC_RATE_SEL_MASK) { | |
312 | case CM_GFX_SEL1: | |
313 | asize = 3; | |
314 | div_array = gfx_div; | |
315 | break; | |
316 | case CM_PLL_SEL1: | |
317 | return omap2_dpll_round_rate(target_rate); | |
318 | case CM_SYSCLKOUT_SEL1: | |
319 | asize = 5; | |
320 | div_array = sysclkout_div; | |
321 | break; | |
322 | case CM_CORE_SEL1: | |
323 | if(tclk == &dss1_fck){ | |
324 | if(tclk->parent == &core_ck){ | |
325 | asize = 10; | |
326 | div_array = dss1_div; | |
327 | } else { | |
328 | *new_div = 0; /* fixed clk */ | |
329 | return(tclk->parent->rate); | |
330 | } | |
331 | } else if((tclk == &vlynq_fck) && cpu_is_omap2420()){ | |
332 | if(tclk->parent == &core_ck){ | |
333 | asize = 10; | |
334 | div_array = vylnq_div; | |
335 | } else { | |
336 | *new_div = 0; /* fixed clk */ | |
337 | return(tclk->parent->rate); | |
338 | } | |
339 | } | |
340 | break; | |
341 | } | |
342 | ||
343 | best_div = omap2_divider_from_table(asize, div_array, | |
344 | tclk->parent->rate, target_rate); | |
345 | if (best_div == ~0){ | |
346 | *new_div = 1; | |
347 | return best_div; /* signal error */ | |
348 | } | |
349 | ||
350 | *new_div = best_div; | |
351 | return (tclk->parent->rate / best_div); | |
352 | } | |
353 | ||
354 | /* Given a clock and a rate apply a clock specific rounding function */ | |
355 | static long omap2_clk_round_rate(struct clk *clk, unsigned long rate) | |
356 | { | |
357 | u32 new_div = 0; | |
358 | int valid_rate; | |
359 | ||
360 | if (clk->flags & RATE_FIXED) | |
361 | return clk->rate; | |
362 | ||
363 | if (clk->flags & RATE_CKCTL) { | |
364 | valid_rate = omap2_clksel_round_rate(clk, rate, &new_div); | |
365 | return valid_rate; | |
366 | } | |
367 | ||
368 | if (clk->round_rate != 0) | |
369 | return clk->round_rate(clk, rate); | |
370 | ||
371 | return clk->rate; | |
372 | } | |
373 | ||
374 | /* | |
375 | * Check the DLL lock state, and return tue if running in unlock mode. | |
376 | * This is needed to compenste for the shifted DLL value in unlock mode. | |
377 | */ | |
378 | static u32 omap2_dll_force_needed(void) | |
379 | { | |
380 | u32 dll_state = SDRC_DLLA_CTRL; /* dlla and dllb are a set */ | |
381 | ||
382 | if ((dll_state & (1 << 2)) == (1 << 2)) | |
383 | return 1; | |
384 | else | |
385 | return 0; | |
386 | } | |
387 | ||
388 | static void omap2_init_memory_params(u32 force_lock_to_unlock_mode) | |
389 | { | |
390 | unsigned long dll_cnt; | |
391 | u32 fast_dll = 0; | |
392 | ||
393 | mem_timings.m_type = !((SDRC_MR_0 & 0x3) == 0x1); /* DDR = 1, SDR = 0 */ | |
394 | ||
395 | /* 2422 es2.05 and beyond has a single SIP DDR instead of 2 like others. | |
396 | * In the case of 2422, its ok to use CS1 instead of CS0. | |
397 | */ | |
398 | ||
399 | #if 0 /* FIXME: Enable after 24xx cpu detection works */ | |
400 | ctype = get_cpu_type(); | |
401 | if (cpu_is_omap2422()) | |
402 | mem_timings.base_cs = 1; | |
403 | else | |
404 | #endif | |
405 | mem_timings.base_cs = 0; | |
406 | ||
407 | if (mem_timings.m_type != M_DDR) | |
408 | return; | |
409 | ||
410 | /* With DDR we need to determine the low frequency DLL value */ | |
411 | if (((mem_timings.fast_dll_ctrl & (1 << 2)) == M_LOCK_CTRL)) | |
412 | mem_timings.dll_mode = M_UNLOCK; | |
413 | else | |
414 | mem_timings.dll_mode = M_LOCK; | |
415 | ||
416 | if (mem_timings.base_cs == 0) { | |
417 | fast_dll = SDRC_DLLA_CTRL; | |
418 | dll_cnt = SDRC_DLLA_STATUS & 0xff00; | |
419 | } else { | |
420 | fast_dll = SDRC_DLLB_CTRL; | |
421 | dll_cnt = SDRC_DLLB_STATUS & 0xff00; | |
422 | } | |
423 | if (force_lock_to_unlock_mode) { | |
424 | fast_dll &= ~0xff00; | |
425 | fast_dll |= dll_cnt; /* Current lock mode */ | |
426 | } | |
427 | mem_timings.fast_dll_ctrl = fast_dll; | |
428 | ||
429 | /* No disruptions, DDR will be offline & C-ABI not followed */ | |
430 | omap2_sram_ddr_init(&mem_timings.slow_dll_ctrl, | |
431 | mem_timings.fast_dll_ctrl, | |
432 | mem_timings.base_cs, | |
433 | force_lock_to_unlock_mode); | |
434 | mem_timings.slow_dll_ctrl &= 0xff00; /* Keep lock value */ | |
435 | ||
436 | /* Turn status into unlock ctrl */ | |
437 | mem_timings.slow_dll_ctrl |= | |
438 | ((mem_timings.fast_dll_ctrl & 0xF) | (1 << 2)); | |
439 | ||
440 | /* 90 degree phase for anything below 133Mhz */ | |
441 | mem_timings.slow_dll_ctrl |= (1 << 1); | |
442 | } | |
443 | ||
444 | static u32 omap2_reprogram_sdrc(u32 level, u32 force) | |
445 | { | |
446 | u32 prev = curr_perf_level, flags; | |
447 | ||
448 | if ((curr_perf_level == level) && !force) | |
449 | return prev; | |
450 | ||
451 | if (level == PRCM_HALF_SPEED) { | |
452 | local_irq_save(flags); | |
453 | PRCM_VOLTSETUP = 0xffff; | |
454 | omap2_sram_reprogram_sdrc(PRCM_HALF_SPEED, | |
455 | mem_timings.slow_dll_ctrl, | |
456 | mem_timings.m_type); | |
457 | curr_perf_level = PRCM_HALF_SPEED; | |
458 | local_irq_restore(flags); | |
459 | } | |
460 | if (level == PRCM_FULL_SPEED) { | |
461 | local_irq_save(flags); | |
462 | PRCM_VOLTSETUP = 0xffff; | |
463 | omap2_sram_reprogram_sdrc(PRCM_FULL_SPEED, | |
464 | mem_timings.fast_dll_ctrl, | |
465 | mem_timings.m_type); | |
466 | curr_perf_level = PRCM_FULL_SPEED; | |
467 | local_irq_restore(flags); | |
468 | } | |
469 | ||
470 | return prev; | |
471 | } | |
472 | ||
473 | static int omap2_reprogram_dpll(struct clk * clk, unsigned long rate) | |
474 | { | |
475 | u32 flags, cur_rate, low, mult, div, valid_rate, done_rate; | |
476 | u32 bypass = 0; | |
477 | struct prcm_config tmpset; | |
478 | int ret = -EINVAL; | |
479 | ||
480 | local_irq_save(flags); | |
481 | cur_rate = omap2_get_dpll_rate(&dpll_ck); | |
482 | mult = CM_CLKSEL2_PLL & 0x3; | |
483 | ||
484 | if ((rate == (cur_rate / 2)) && (mult == 2)) { | |
485 | omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1); | |
486 | } else if ((rate == (cur_rate * 2)) && (mult == 1)) { | |
487 | omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); | |
488 | } else if (rate != cur_rate) { | |
489 | valid_rate = omap2_dpll_round_rate(rate); | |
490 | if (valid_rate != rate) | |
491 | goto dpll_exit; | |
492 | ||
493 | if ((CM_CLKSEL2_PLL & 0x3) == 1) | |
494 | low = curr_prcm_set->dpll_speed; | |
495 | else | |
496 | low = curr_prcm_set->dpll_speed / 2; | |
497 | ||
498 | tmpset.cm_clksel1_pll = CM_CLKSEL1_PLL; | |
499 | tmpset.cm_clksel1_pll &= ~(0x3FFF << 8); | |
500 | div = ((curr_prcm_set->xtal_speed / 1000000) - 1); | |
501 | tmpset.cm_clksel2_pll = CM_CLKSEL2_PLL; | |
502 | tmpset.cm_clksel2_pll &= ~0x3; | |
503 | if (rate > low) { | |
504 | tmpset.cm_clksel2_pll |= 0x2; | |
505 | mult = ((rate / 2) / 1000000); | |
506 | done_rate = PRCM_FULL_SPEED; | |
507 | } else { | |
508 | tmpset.cm_clksel2_pll |= 0x1; | |
509 | mult = (rate / 1000000); | |
510 | done_rate = PRCM_HALF_SPEED; | |
511 | } | |
512 | tmpset.cm_clksel1_pll |= ((div << 8) | (mult << 12)); | |
513 | ||
514 | /* Worst case */ | |
515 | tmpset.base_sdrc_rfr = V24XX_SDRC_RFR_CTRL_BYPASS; | |
516 | ||
517 | if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */ | |
518 | bypass = 1; | |
519 | ||
520 | omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); /* For init_mem */ | |
521 | ||
522 | /* Force dll lock mode */ | |
523 | omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr, | |
524 | bypass); | |
525 | ||
526 | /* Errata: ret dll entry state */ | |
527 | omap2_init_memory_params(omap2_dll_force_needed()); | |
528 | omap2_reprogram_sdrc(done_rate, 0); | |
529 | } | |
530 | omap2_clksel_recalc(&dpll_ck); | |
531 | ret = 0; | |
532 | ||
533 | dpll_exit: | |
534 | local_irq_restore(flags); | |
535 | return(ret); | |
536 | } | |
537 | ||
538 | /* Just return the MPU speed */ | |
539 | static void omap2_mpu_recalc(struct clk * clk) | |
540 | { | |
541 | clk->rate = curr_prcm_set->mpu_speed; | |
542 | } | |
543 | ||
544 | /* | |
545 | * Look for a rate equal or less than the target rate given a configuration set. | |
546 | * | |
547 | * What's not entirely clear is "which" field represents the key field. | |
548 | * Some might argue L3-DDR, others ARM, others IVA. This code is simple and | |
549 | * just uses the ARM rates. | |
550 | */ | |
551 | static long omap2_round_to_table_rate(struct clk * clk, unsigned long rate) | |
552 | { | |
553 | struct prcm_config * ptr; | |
554 | long highest_rate; | |
555 | ||
556 | if (clk != &virt_prcm_set) | |
557 | return -EINVAL; | |
558 | ||
559 | highest_rate = -EINVAL; | |
560 | ||
561 | for (ptr = rate_table; ptr->mpu_speed; ptr++) { | |
562 | if (ptr->xtal_speed != sys_ck.rate) | |
563 | continue; | |
564 | ||
565 | highest_rate = ptr->mpu_speed; | |
566 | ||
567 | /* Can check only after xtal frequency check */ | |
568 | if (ptr->mpu_speed <= rate) | |
569 | break; | |
570 | } | |
571 | return highest_rate; | |
572 | } | |
573 | ||
574 | /* | |
575 | * omap2_convert_field_to_div() - turn field value into integer divider | |
576 | */ | |
577 | static u32 omap2_clksel_to_divisor(u32 div_sel, u32 field_val) | |
578 | { | |
579 | u32 i; | |
580 | u32 clkout_array[] = {1, 2, 4, 8, 16}; | |
581 | ||
582 | if ((div_sel & SRC_RATE_SEL_MASK) == CM_SYSCLKOUT_SEL1) { | |
583 | for (i = 0; i < 5; i++) { | |
584 | if (field_val == i) | |
585 | return clkout_array[i]; | |
586 | } | |
587 | return ~0; | |
588 | } else | |
589 | return field_val; | |
590 | } | |
591 | ||
592 | /* | |
593 | * Returns the CLKSEL divider register value | |
594 | * REVISIT: This should be cleaned up to work nicely with void __iomem * | |
595 | */ | |
596 | static u32 omap2_get_clksel(u32 *div_sel, u32 *field_mask, | |
597 | struct clk *clk) | |
598 | { | |
599 | int ret = ~0; | |
600 | u32 reg_val, div_off; | |
601 | u32 div_addr = 0; | |
602 | u32 mask = ~0; | |
603 | ||
604 | div_off = clk->rate_offset; | |
605 | ||
606 | switch ((*div_sel & SRC_RATE_SEL_MASK)) { | |
607 | case CM_MPU_SEL1: | |
608 | div_addr = (u32)&CM_CLKSEL_MPU; | |
609 | mask = 0x1f; | |
610 | break; | |
611 | case CM_DSP_SEL1: | |
612 | div_addr = (u32)&CM_CLKSEL_DSP; | |
613 | if (cpu_is_omap2420()) { | |
614 | if ((div_off == 0) || (div_off == 8)) | |
615 | mask = 0x1f; | |
616 | else if (div_off == 5) | |
617 | mask = 0x3; | |
618 | } else if (cpu_is_omap2430()) { | |
619 | if (div_off == 0) | |
620 | mask = 0x1f; | |
621 | else if (div_off == 5) | |
622 | mask = 0x3; | |
623 | } | |
624 | break; | |
625 | case CM_GFX_SEL1: | |
626 | div_addr = (u32)&CM_CLKSEL_GFX; | |
627 | if (div_off == 0) | |
628 | mask = 0x7; | |
629 | break; | |
630 | case CM_MODEM_SEL1: | |
631 | div_addr = (u32)&CM_CLKSEL_MDM; | |
632 | if (div_off == 0) | |
633 | mask = 0xf; | |
634 | break; | |
635 | case CM_SYSCLKOUT_SEL1: | |
636 | div_addr = (u32)&PRCM_CLKOUT_CTRL; | |
637 | if ((div_off == 3) || (div_off = 11)) | |
638 | mask= 0x3; | |
639 | break; | |
640 | case CM_CORE_SEL1: | |
641 | div_addr = (u32)&CM_CLKSEL1_CORE; | |
642 | switch (div_off) { | |
643 | case 0: /* l3 */ | |
644 | case 8: /* dss1 */ | |
645 | case 15: /* vylnc-2420 */ | |
646 | case 20: /* ssi */ | |
647 | mask = 0x1f; break; | |
648 | case 5: /* l4 */ | |
649 | mask = 0x3; break; | |
650 | case 13: /* dss2 */ | |
651 | mask = 0x1; break; | |
652 | case 25: /* usb */ | |
653 | mask = 0xf; break; | |
654 | } | |
655 | } | |
656 | ||
657 | *field_mask = mask; | |
658 | ||
659 | if (unlikely(mask == ~0)) | |
660 | div_addr = 0; | |
661 | ||
662 | *div_sel = div_addr; | |
663 | ||
664 | if (unlikely(div_addr == 0)) | |
665 | return ret; | |
666 | ||
667 | /* Isolate field */ | |
668 | reg_val = __raw_readl((void __iomem *)div_addr) & (mask << div_off); | |
669 | ||
670 | /* Normalize back to divider value */ | |
671 | reg_val >>= div_off; | |
672 | ||
673 | return reg_val; | |
674 | } | |
675 | ||
676 | /* | |
677 | * Return divider to be applied to parent clock. | |
678 | * Return 0 on error. | |
679 | */ | |
680 | static u32 omap2_clksel_get_divisor(struct clk *clk) | |
681 | { | |
682 | int ret = 0; | |
683 | u32 div, div_sel, div_off, field_mask, field_val; | |
684 | ||
685 | /* isolate control register */ | |
686 | div_sel = (SRC_RATE_SEL_MASK & clk->flags); | |
687 | ||
688 | div_off = clk->rate_offset; | |
689 | field_val = omap2_get_clksel(&div_sel, &field_mask, clk); | |
690 | if (div_sel == 0) | |
691 | return ret; | |
692 | ||
693 | div_sel = (SRC_RATE_SEL_MASK & clk->flags); | |
694 | div = omap2_clksel_to_divisor(div_sel, field_val); | |
695 | ||
696 | return div; | |
697 | } | |
698 | ||
699 | /* Set the clock rate for a clock source */ | |
700 | static int omap2_clk_set_rate(struct clk *clk, unsigned long rate) | |
701 | ||
702 | { | |
703 | int ret = -EINVAL; | |
704 | void __iomem * reg; | |
705 | u32 div_sel, div_off, field_mask, field_val, reg_val, validrate; | |
706 | u32 new_div = 0; | |
707 | ||
708 | if (!(clk->flags & CONFIG_PARTICIPANT) && (clk->flags & RATE_CKCTL)) { | |
709 | if (clk == &dpll_ck) | |
710 | return omap2_reprogram_dpll(clk, rate); | |
711 | ||
712 | /* Isolate control register */ | |
713 | div_sel = (SRC_RATE_SEL_MASK & clk->flags); | |
714 | div_off = clk->src_offset; | |
715 | ||
716 | validrate = omap2_clksel_round_rate(clk, rate, &new_div); | |
717 | if(validrate != rate) | |
718 | return(ret); | |
719 | ||
720 | field_val = omap2_get_clksel(&div_sel, &field_mask, clk); | |
721 | if (div_sel == 0) | |
722 | return ret; | |
723 | ||
724 | if(clk->flags & CM_SYSCLKOUT_SEL1){ | |
725 | switch(new_div){ | |
726 | case 16: field_val = 4; break; | |
727 | case 8: field_val = 3; break; | |
728 | case 4: field_val = 2; break; | |
729 | case 2: field_val = 1; break; | |
730 | case 1: field_val = 0; break; | |
731 | } | |
732 | } | |
733 | else | |
734 | field_val = new_div; | |
735 | ||
736 | reg = (void __iomem *)div_sel; | |
737 | ||
738 | reg_val = __raw_readl(reg); | |
739 | reg_val &= ~(field_mask << div_off); | |
740 | reg_val |= (field_val << div_off); | |
741 | ||
742 | __raw_writel(reg_val, reg); | |
743 | clk->rate = clk->parent->rate / field_val; | |
744 | ||
745 | if (clk->flags & DELAYED_APP) | |
746 | __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); | |
747 | ret = 0; | |
748 | } else if (clk->set_rate != 0) | |
749 | ret = clk->set_rate(clk, rate); | |
750 | ||
751 | if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) | |
752 | propagate_rate(clk); | |
753 | ||
754 | return ret; | |
755 | } | |
756 | ||
757 | /* Converts encoded control register address into a full address */ | |
758 | static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset, | |
759 | struct clk *src_clk, u32 *field_mask) | |
760 | { | |
761 | u32 val = ~0, src_reg_addr = 0, mask = 0; | |
762 | ||
763 | /* Find target control register.*/ | |
764 | switch ((*type_to_addr & SRC_RATE_SEL_MASK)) { | |
765 | case CM_CORE_SEL1: | |
766 | src_reg_addr = (u32)&CM_CLKSEL1_CORE; | |
767 | if (reg_offset == 13) { /* DSS2_fclk */ | |
768 | mask = 0x1; | |
769 | if (src_clk == &sys_ck) | |
770 | val = 0; | |
771 | if (src_clk == &func_48m_ck) | |
772 | val = 1; | |
773 | } else if (reg_offset == 8) { /* DSS1_fclk */ | |
774 | mask = 0x1f; | |
775 | if (src_clk == &sys_ck) | |
776 | val = 0; | |
777 | else if (src_clk == &core_ck) /* divided clock */ | |
778 | val = 0x10; /* rate needs fixing */ | |
779 | } else if ((reg_offset == 15) && cpu_is_omap2420()){ /*vlnyq*/ | |
780 | mask = 0x1F; | |
781 | if(src_clk == &func_96m_ck) | |
782 | val = 0; | |
783 | else if (src_clk == &core_ck) | |
784 | val = 0x10; | |
785 | } | |
786 | break; | |
787 | case CM_CORE_SEL2: | |
788 | src_reg_addr = (u32)&CM_CLKSEL2_CORE; | |
789 | mask = 0x3; | |
790 | if (src_clk == &func_32k_ck) | |
791 | val = 0x0; | |
792 | if (src_clk == &sys_ck) | |
793 | val = 0x1; | |
794 | if (src_clk == &alt_ck) | |
795 | val = 0x2; | |
796 | break; | |
797 | case CM_WKUP_SEL1: | |
798 | src_reg_addr = (u32)&CM_CLKSEL2_CORE; | |
799 | mask = 0x3; | |
800 | if (src_clk == &func_32k_ck) | |
801 | val = 0x0; | |
802 | if (src_clk == &sys_ck) | |
803 | val = 0x1; | |
804 | if (src_clk == &alt_ck) | |
805 | val = 0x2; | |
806 | break; | |
807 | case CM_PLL_SEL1: | |
808 | src_reg_addr = (u32)&CM_CLKSEL1_PLL; | |
809 | mask = 0x1; | |
810 | if (reg_offset == 0x3) { | |
811 | if (src_clk == &apll96_ck) | |
812 | val = 0; | |
813 | if (src_clk == &alt_ck) | |
814 | val = 1; | |
815 | } | |
816 | else if (reg_offset == 0x5) { | |
817 | if (src_clk == &apll54_ck) | |
818 | val = 0; | |
819 | if (src_clk == &alt_ck) | |
820 | val = 1; | |
821 | } | |
822 | break; | |
823 | case CM_PLL_SEL2: | |
824 | src_reg_addr = (u32)&CM_CLKSEL2_PLL; | |
825 | mask = 0x3; | |
826 | if (src_clk == &func_32k_ck) | |
827 | val = 0x0; | |
828 | if (src_clk == &dpll_ck) | |
829 | val = 0x2; | |
830 | break; | |
831 | case CM_SYSCLKOUT_SEL1: | |
832 | src_reg_addr = (u32)&PRCM_CLKOUT_CTRL; | |
833 | mask = 0x3; | |
834 | if (src_clk == &dpll_ck) | |
835 | val = 0; | |
836 | if (src_clk == &sys_ck) | |
837 | val = 1; | |
838 | if (src_clk == &func_54m_ck) | |
839 | val = 2; | |
840 | if (src_clk == &func_96m_ck) | |
841 | val = 3; | |
842 | break; | |
843 | } | |
844 | ||
845 | if (val == ~0) /* Catch errors in offset */ | |
846 | *type_to_addr = 0; | |
847 | else | |
848 | *type_to_addr = src_reg_addr; | |
849 | *field_mask = mask; | |
850 | ||
851 | return val; | |
852 | } | |
853 | ||
854 | static int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) | |
855 | { | |
856 | void __iomem * reg; | |
857 | u32 src_sel, src_off, field_val, field_mask, reg_val, rate; | |
858 | int ret = -EINVAL; | |
859 | ||
860 | if (unlikely(clk->flags & CONFIG_PARTICIPANT)) | |
861 | return ret; | |
862 | ||
863 | if (clk->flags & SRC_SEL_MASK) { /* On-chip SEL collection */ | |
864 | src_sel = (SRC_RATE_SEL_MASK & clk->flags); | |
865 | src_off = clk->src_offset; | |
866 | ||
867 | if (src_sel == 0) | |
868 | goto set_parent_error; | |
869 | ||
870 | field_val = omap2_get_src_field(&src_sel, src_off, new_parent, | |
871 | &field_mask); | |
872 | ||
873 | reg = (void __iomem *)src_sel; | |
874 | ||
875 | if (clk->usecount > 0) | |
876 | omap2_clk_disable(clk); | |
877 | ||
878 | /* Set new source value (previous dividers if any in effect) */ | |
879 | reg_val = __raw_readl(reg) & ~(field_mask << src_off); | |
880 | reg_val |= (field_val << src_off); | |
881 | __raw_writel(reg_val, reg); | |
882 | ||
883 | if (clk->flags & DELAYED_APP) | |
884 | __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); | |
885 | ||
886 | if (clk->usecount > 0) | |
887 | omap2_clk_enable(clk); | |
888 | ||
889 | clk->parent = new_parent; | |
890 | ||
891 | /* SRC_RATE_SEL_MASK clocks follow their parents rates.*/ | |
892 | if ((new_parent == &core_ck) && (clk == &dss1_fck)) | |
893 | clk->rate = new_parent->rate / 0x10; | |
894 | else | |
895 | clk->rate = new_parent->rate; | |
896 | ||
897 | if (unlikely(clk->flags & RATE_PROPAGATES)) | |
898 | propagate_rate(clk); | |
899 | ||
900 | return 0; | |
901 | } else { | |
902 | clk->parent = new_parent; | |
903 | rate = new_parent->rate; | |
904 | omap2_clk_set_rate(clk, rate); | |
905 | ret = 0; | |
906 | } | |
907 | ||
908 | set_parent_error: | |
909 | return ret; | |
910 | } | |
911 | ||
912 | /* Sets basic clocks based on the specified rate */ | |
913 | static int omap2_select_table_rate(struct clk * clk, unsigned long rate) | |
914 | { | |
915 | u32 flags, cur_rate, done_rate, bypass = 0; | |
916 | u8 cpu_mask = 0; | |
917 | struct prcm_config *prcm; | |
918 | unsigned long found_speed = 0; | |
919 | ||
920 | if (clk != &virt_prcm_set) | |
921 | return -EINVAL; | |
922 | ||
923 | /* FIXME: Change cpu_is_omap2420() to cpu_is_omap242x() */ | |
924 | if (cpu_is_omap2420()) | |
925 | cpu_mask = RATE_IN_242X; | |
926 | else if (cpu_is_omap2430()) | |
927 | cpu_mask = RATE_IN_243X; | |
928 | ||
929 | for (prcm = rate_table; prcm->mpu_speed; prcm++) { | |
930 | if (!(prcm->flags & cpu_mask)) | |
931 | continue; | |
932 | ||
933 | if (prcm->xtal_speed != sys_ck.rate) | |
934 | continue; | |
935 | ||
936 | if (prcm->mpu_speed <= rate) { | |
937 | found_speed = prcm->mpu_speed; | |
938 | break; | |
939 | } | |
940 | } | |
941 | ||
942 | if (!found_speed) { | |
943 | printk(KERN_INFO "Could not set MPU rate to %luMHz\n", | |
944 | rate / 1000000); | |
945 | return -EINVAL; | |
946 | } | |
947 | ||
948 | curr_prcm_set = prcm; | |
949 | cur_rate = omap2_get_dpll_rate(&dpll_ck); | |
950 | ||
951 | if (prcm->dpll_speed == cur_rate / 2) { | |
952 | omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1); | |
953 | } else if (prcm->dpll_speed == cur_rate * 2) { | |
954 | omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); | |
955 | } else if (prcm->dpll_speed != cur_rate) { | |
956 | local_irq_save(flags); | |
957 | ||
958 | if (prcm->dpll_speed == prcm->xtal_speed) | |
959 | bypass = 1; | |
960 | ||
961 | if ((prcm->cm_clksel2_pll & 0x3) == 2) | |
962 | done_rate = PRCM_FULL_SPEED; | |
963 | else | |
964 | done_rate = PRCM_HALF_SPEED; | |
965 | ||
966 | /* MPU divider */ | |
967 | CM_CLKSEL_MPU = prcm->cm_clksel_mpu; | |
968 | ||
969 | /* dsp + iva1 div(2420), iva2.1(2430) */ | |
970 | CM_CLKSEL_DSP = prcm->cm_clksel_dsp; | |
971 | ||
972 | CM_CLKSEL_GFX = prcm->cm_clksel_gfx; | |
973 | ||
974 | /* Major subsystem dividers */ | |
975 | CM_CLKSEL1_CORE = prcm->cm_clksel1_core; | |
976 | if (cpu_is_omap2430()) | |
977 | CM_CLKSEL_MDM = prcm->cm_clksel_mdm; | |
978 | ||
979 | /* x2 to enter init_mem */ | |
980 | omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); | |
981 | ||
982 | omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr, | |
983 | bypass); | |
984 | ||
985 | omap2_init_memory_params(omap2_dll_force_needed()); | |
986 | omap2_reprogram_sdrc(done_rate, 0); | |
987 | ||
988 | local_irq_restore(flags); | |
989 | } | |
990 | omap2_clksel_recalc(&dpll_ck); | |
991 | ||
992 | return 0; | |
993 | } | |
994 | ||
995 | /*------------------------------------------------------------------------- | |
996 | * Omap2 clock reset and init functions | |
997 | *-------------------------------------------------------------------------*/ | |
998 | ||
999 | static struct clk_functions omap2_clk_functions = { | |
1000 | .clk_enable = omap2_clk_enable, | |
1001 | .clk_disable = omap2_clk_disable, | |
1002 | .clk_use = omap2_clk_use, | |
1003 | .clk_unuse = omap2_clk_unuse, | |
1004 | .clk_round_rate = omap2_clk_round_rate, | |
1005 | .clk_set_rate = omap2_clk_set_rate, | |
1006 | .clk_set_parent = omap2_clk_set_parent, | |
1007 | }; | |
1008 | ||
1009 | static void __init omap2_get_crystal_rate(struct clk *osc, struct clk *sys) | |
1010 | { | |
1011 | u32 div, aplls, sclk = 13000000; | |
1012 | ||
1013 | aplls = CM_CLKSEL1_PLL; | |
1014 | aplls &= ((1 << 23) | (1 << 24) | (1 << 25)); | |
1015 | aplls >>= 23; /* Isolate field, 0,2,3 */ | |
1016 | ||
1017 | if (aplls == 0) | |
1018 | sclk = 19200000; | |
1019 | else if (aplls == 2) | |
1020 | sclk = 13000000; | |
1021 | else if (aplls == 3) | |
1022 | sclk = 12000000; | |
1023 | ||
1024 | div = PRCM_CLKSRC_CTRL; | |
1025 | div &= ((1 << 7) | (1 << 6)); | |
1026 | div >>= sys->rate_offset; | |
1027 | ||
1028 | osc->rate = sclk * div; | |
1029 | sys->rate = sclk; | |
1030 | } | |
1031 | ||
1032 | #ifdef CONFIG_OMAP_RESET_CLOCKS | |
1033 | static void __init omap2_disable_unused_clocks(void) | |
1034 | { | |
1035 | struct clk *ck; | |
1036 | u32 regval32; | |
1037 | ||
1038 | list_for_each_entry(ck, &clocks, node) { | |
1039 | if (ck->usecount > 0 || (ck->flags & ALWAYS_ENABLED) || | |
1040 | ck->enable_reg == 0) | |
1041 | continue; | |
1042 | ||
1043 | regval32 = __raw_readl(ck->enable_reg); | |
1044 | if ((regval32 & (1 << ck->enable_bit)) == 0) | |
1045 | continue; | |
1046 | ||
1047 | printk(KERN_INFO "Disabling unused clock \"%s\"\n", ck->name); | |
1048 | omap2_clk_disable(ck); | |
1049 | } | |
1050 | } | |
1051 | late_initcall(omap2_disable_unused_clocks); | |
1052 | #endif | |
1053 | ||
1054 | /* | |
1055 | * Switch the MPU rate if specified on cmdline. | |
1056 | * We cannot do this early until cmdline is parsed. | |
1057 | */ | |
1058 | static int __init omap2_clk_arch_init(void) | |
1059 | { | |
1060 | if (!mpurate) | |
1061 | return -EINVAL; | |
1062 | ||
1063 | if (omap2_select_table_rate(&virt_prcm_set, mpurate)) | |
1064 | printk(KERN_ERR "Could not find matching MPU rate\n"); | |
1065 | ||
1066 | propagate_rate(&osc_ck); /* update main root fast */ | |
1067 | propagate_rate(&func_32k_ck); /* update main root slow */ | |
1068 | ||
1069 | printk(KERN_INFO "Switched to new clocking rate (Crystal/DPLL/MPU): " | |
1070 | "%ld.%01ld/%ld/%ld MHz\n", | |
1071 | (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10, | |
1072 | (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ; | |
1073 | ||
1074 | return 0; | |
1075 | } | |
1076 | arch_initcall(omap2_clk_arch_init); | |
1077 | ||
1078 | int __init omap2_clk_init(void) | |
1079 | { | |
1080 | struct prcm_config *prcm; | |
1081 | struct clk ** clkp; | |
1082 | u32 clkrate; | |
1083 | ||
1084 | clk_init(&omap2_clk_functions); | |
1085 | omap2_get_crystal_rate(&osc_ck, &sys_ck); | |
1086 | ||
1087 | for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks); | |
1088 | clkp++) { | |
1089 | ||
1090 | if ((*clkp)->flags & CLOCK_IN_OMAP242X && cpu_is_omap2420()) { | |
1091 | clk_register(*clkp); | |
1092 | continue; | |
1093 | } | |
1094 | ||
1095 | if ((*clkp)->flags & CLOCK_IN_OMAP243X && cpu_is_omap2430()) { | |
1096 | clk_register(*clkp); | |
1097 | continue; | |
1098 | } | |
1099 | } | |
1100 | ||
1101 | /* Check the MPU rate set by bootloader */ | |
1102 | clkrate = omap2_get_dpll_rate(&dpll_ck); | |
1103 | for (prcm = rate_table; prcm->mpu_speed; prcm++) { | |
1104 | if (prcm->xtal_speed != sys_ck.rate) | |
1105 | continue; | |
1106 | if (prcm->dpll_speed <= clkrate) | |
1107 | break; | |
1108 | } | |
1109 | curr_prcm_set = prcm; | |
1110 | ||
1111 | propagate_rate(&osc_ck); /* update main root fast */ | |
1112 | propagate_rate(&func_32k_ck); /* update main root slow */ | |
1113 | ||
1114 | printk(KERN_INFO "Clocking rate (Crystal/DPLL/MPU): " | |
1115 | "%ld.%01ld/%ld/%ld MHz\n", | |
1116 | (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10, | |
1117 | (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ; | |
1118 | ||
1119 | /* | |
1120 | * Only enable those clocks we will need, let the drivers | |
1121 | * enable other clocks as necessary | |
1122 | */ | |
1123 | clk_use(&sync_32k_ick); | |
1124 | clk_use(&omapctrl_ick); | |
1125 | if (cpu_is_omap2430()) | |
1126 | clk_use(&sdrc_ick); | |
1127 | ||
1128 | return 0; | |
1129 | } |