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