diff options
-rw-r--r-- | arch/arm/mach-omap2/clock.c | 757 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.h | 73 |
2 files changed, 830 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c new file mode 100644 index 000000000000..a921efd43a70 --- /dev/null +++ b/arch/arm/mach-omap2/clock.c | |||
@@ -0,0 +1,757 @@ | |||
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 | * Copyright (C) 2007 Texas Instruments, Inc. | ||
12 | * Copyright (C) 2007 Nokia Corporation | ||
13 | * Paul Walmsley | ||
14 | * | ||
15 | * Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation | ||
16 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
17 | * | ||
18 | * This program is free software; you can redistribute it and/or modify | ||
19 | * it under the terms of the GNU General Public License version 2 as | ||
20 | * published by the Free Software Foundation. | ||
21 | */ | ||
22 | #undef DEBUG | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/device.h> | ||
27 | #include <linux/list.h> | ||
28 | #include <linux/errno.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <asm/bitops.h> | ||
32 | |||
33 | #include <asm/io.h> | ||
34 | |||
35 | #include <asm/arch/clock.h> | ||
36 | #include <asm/arch/sram.h> | ||
37 | #include <asm/arch/cpu.h> | ||
38 | #include <asm/div64.h> | ||
39 | |||
40 | #include "memory.h" | ||
41 | #include "sdrc.h" | ||
42 | #include "clock.h" | ||
43 | #include "prm.h" | ||
44 | #include "prm-regbits-24xx.h" | ||
45 | #include "cm.h" | ||
46 | #include "cm-regbits-24xx.h" | ||
47 | #include "cm-regbits-34xx.h" | ||
48 | |||
49 | #define MAX_CLOCK_ENABLE_WAIT 100000 | ||
50 | |||
51 | u8 cpu_mask; | ||
52 | |||
53 | /*------------------------------------------------------------------------- | ||
54 | * Omap2 specific clock functions | ||
55 | *-------------------------------------------------------------------------*/ | ||
56 | |||
57 | /** | ||
58 | * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware | ||
59 | * @clk: OMAP clock struct ptr to use | ||
60 | * | ||
61 | * Given a pointer to a source-selectable struct clk, read the hardware | ||
62 | * register and determine what its parent is currently set to. Update the | ||
63 | * clk->parent field with the appropriate clk ptr. | ||
64 | */ | ||
65 | void omap2_init_clksel_parent(struct clk *clk) | ||
66 | { | ||
67 | const struct clksel *clks; | ||
68 | const struct clksel_rate *clkr; | ||
69 | u32 r, found = 0; | ||
70 | |||
71 | if (!clk->clksel) | ||
72 | return; | ||
73 | |||
74 | r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | ||
75 | r >>= __ffs(clk->clksel_mask); | ||
76 | |||
77 | for (clks = clk->clksel; clks->parent && !found; clks++) { | ||
78 | for (clkr = clks->rates; clkr->div && !found; clkr++) { | ||
79 | if ((clkr->flags & cpu_mask) && (clkr->val == r)) { | ||
80 | if (clk->parent != clks->parent) { | ||
81 | pr_debug("clock: inited %s parent " | ||
82 | "to %s (was %s)\n", | ||
83 | clk->name, clks->parent->name, | ||
84 | ((clk->parent) ? | ||
85 | clk->parent->name : "NULL")); | ||
86 | clk->parent = clks->parent; | ||
87 | }; | ||
88 | found = 1; | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (!found) | ||
94 | printk(KERN_ERR "clock: init parent: could not find " | ||
95 | "regval %0x for clock %s\n", r, clk->name); | ||
96 | |||
97 | return; | ||
98 | } | ||
99 | |||
100 | /* Returns the DPLL rate */ | ||
101 | u32 omap2_get_dpll_rate(struct clk *clk) | ||
102 | { | ||
103 | long long dpll_clk; | ||
104 | u32 dpll_mult, dpll_div, dpll; | ||
105 | const struct dpll_data *dd; | ||
106 | |||
107 | dd = clk->dpll_data; | ||
108 | /* REVISIT: What do we return on error? */ | ||
109 | if (!dd) | ||
110 | return 0; | ||
111 | |||
112 | dpll = __raw_readl(dd->mult_div1_reg); | ||
113 | dpll_mult = dpll & dd->mult_mask; | ||
114 | dpll_mult >>= __ffs(dd->mult_mask); | ||
115 | dpll_div = dpll & dd->div1_mask; | ||
116 | dpll_div >>= __ffs(dd->div1_mask); | ||
117 | |||
118 | dpll_clk = (long long)clk->parent->rate * dpll_mult; | ||
119 | do_div(dpll_clk, dpll_div + 1); | ||
120 | |||
121 | /* 34XX only */ | ||
122 | if (dd->div2_reg) { | ||
123 | dpll = __raw_readl(dd->div2_reg); | ||
124 | dpll_div = dpll & dd->div2_mask; | ||
125 | dpll_div >>= __fss(dd->div2_mask); | ||
126 | do_div(dpll_clk, dpll_div + 1); | ||
127 | } | ||
128 | |||
129 | return dpll_clk; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * Used for clocks that have the same value as the parent clock, | ||
134 | * divided by some factor | ||
135 | */ | ||
136 | void omap2_fixed_divisor_recalc(struct clk *clk) | ||
137 | { | ||
138 | WARN_ON(!clk->fixed_div); | ||
139 | |||
140 | clk->rate = clk->parent->rate / clk->fixed_div; | ||
141 | |||
142 | if (clk->flags & RATE_PROPAGATES) | ||
143 | propagate_rate(clk); | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * omap2_wait_clock_ready - wait for clock to enable | ||
148 | * @reg: physical address of clock IDLEST register | ||
149 | * @mask: value to mask against to determine if the clock is active | ||
150 | * @name: name of the clock (for printk) | ||
151 | * | ||
152 | * Returns 1 if the clock enabled in time, or 0 if it failed to enable | ||
153 | * in roughly MAX_CLOCK_ENABLE_WAIT microseconds. | ||
154 | */ | ||
155 | int omap2_wait_clock_ready(void __iomem *reg, u32 mask, const char *name) | ||
156 | { | ||
157 | int i = 0; | ||
158 | int ena = 0; | ||
159 | |||
160 | /* | ||
161 | * 24xx uses 0 to indicate not ready, and 1 to indicate ready. | ||
162 | * 34xx reverses this, just to keep us on our toes | ||
163 | */ | ||
164 | if (cpu_mask & (RATE_IN_242X | RATE_IN_243X)) { | ||
165 | ena = mask; | ||
166 | } else if (cpu_mask & RATE_IN_343X) { | ||
167 | ena = 0; | ||
168 | } | ||
169 | |||
170 | /* Wait for lock */ | ||
171 | while (((__raw_readl(reg) & mask) != ena) && | ||
172 | (i++ < MAX_CLOCK_ENABLE_WAIT)) { | ||
173 | udelay(1); | ||
174 | } | ||
175 | |||
176 | if (i < MAX_CLOCK_ENABLE_WAIT) | ||
177 | pr_debug("Clock %s stable after %d loops\n", name, i); | ||
178 | else | ||
179 | printk(KERN_ERR "Clock %s didn't enable in %d tries\n", | ||
180 | name, MAX_CLOCK_ENABLE_WAIT); | ||
181 | |||
182 | |||
183 | return (i < MAX_CLOCK_ENABLE_WAIT) ? 1 : 0; | ||
184 | }; | ||
185 | |||
186 | |||
187 | /* | ||
188 | * Note: We don't need special code here for INVERT_ENABLE | ||
189 | * for the time being since INVERT_ENABLE only applies to clocks enabled by | ||
190 | * CM_CLKEN_PLL | ||
191 | */ | ||
192 | static void omap2_clk_wait_ready(struct clk *clk) | ||
193 | { | ||
194 | void __iomem *reg, *other_reg, *st_reg; | ||
195 | u32 bit; | ||
196 | |||
197 | /* | ||
198 | * REVISIT: This code is pretty ugly. It would be nice to generalize | ||
199 | * it and pull it into struct clk itself somehow. | ||
200 | */ | ||
201 | reg = clk->enable_reg; | ||
202 | if ((((u32)reg & 0xff) >= CM_FCLKEN1) && | ||
203 | (((u32)reg & 0xff) <= OMAP24XX_CM_FCLKEN2)) | ||
204 | other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x10); /* CM_ICLKEN* */ | ||
205 | else if ((((u32)reg & 0xff) >= CM_ICLKEN1) && | ||
206 | (((u32)reg & 0xff) <= OMAP24XX_CM_ICLKEN4)) | ||
207 | other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x00); /* CM_FCLKEN* */ | ||
208 | else | ||
209 | return; | ||
210 | |||
211 | /* REVISIT: What are the appropriate exclusions for 34XX? */ | ||
212 | /* No check for DSS or cam clocks */ | ||
213 | if (cpu_is_omap24xx() && ((u32)reg & 0x0f) == 0) { /* CM_{F,I}CLKEN1 */ | ||
214 | if (clk->enable_bit == OMAP24XX_EN_DSS2_SHIFT || | ||
215 | clk->enable_bit == OMAP24XX_EN_DSS1_SHIFT || | ||
216 | clk->enable_bit == OMAP24XX_EN_CAM_SHIFT) | ||
217 | return; | ||
218 | } | ||
219 | |||
220 | /* REVISIT: What are the appropriate exclusions for 34XX? */ | ||
221 | /* OMAP3: ignore DSS-mod clocks */ | ||
222 | if (cpu_is_omap34xx() && | ||
223 | (((u32)reg & ~0xff) == (u32)OMAP_CM_REGADDR(OMAP3430_DSS_MOD, 0))) | ||
224 | return; | ||
225 | |||
226 | /* Check if both functional and interface clocks | ||
227 | * are running. */ | ||
228 | bit = 1 << clk->enable_bit; | ||
229 | if (!(__raw_readl(other_reg) & bit)) | ||
230 | return; | ||
231 | st_reg = (void __iomem *)(((u32)other_reg & ~0xf0) | 0x20); /* CM_IDLEST* */ | ||
232 | |||
233 | omap2_wait_clock_ready(st_reg, bit, clk->name); | ||
234 | } | ||
235 | |||
236 | /* Enables clock without considering parent dependencies or use count | ||
237 | * REVISIT: Maybe change this to use clk->enable like on omap1? | ||
238 | */ | ||
239 | int _omap2_clk_enable(struct clk *clk) | ||
240 | { | ||
241 | u32 regval32; | ||
242 | |||
243 | if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK)) | ||
244 | return 0; | ||
245 | |||
246 | if (clk->enable) | ||
247 | return clk->enable(clk); | ||
248 | |||
249 | if (unlikely(clk->enable_reg == 0)) { | ||
250 | printk(KERN_ERR "clock.c: Enable for %s without enable code\n", | ||
251 | clk->name); | ||
252 | return 0; /* REVISIT: -EINVAL */ | ||
253 | } | ||
254 | |||
255 | regval32 = __raw_readl(clk->enable_reg); | ||
256 | if (clk->flags & INVERT_ENABLE) | ||
257 | regval32 &= ~(1 << clk->enable_bit); | ||
258 | else | ||
259 | regval32 |= (1 << clk->enable_bit); | ||
260 | __raw_writel(regval32, clk->enable_reg); | ||
261 | wmb(); | ||
262 | |||
263 | omap2_clk_wait_ready(clk); | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | /* Disables clock without considering parent dependencies or use count */ | ||
269 | void _omap2_clk_disable(struct clk *clk) | ||
270 | { | ||
271 | u32 regval32; | ||
272 | |||
273 | if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK)) | ||
274 | return; | ||
275 | |||
276 | if (clk->disable) { | ||
277 | clk->disable(clk); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | if (clk->enable_reg == 0) { | ||
282 | /* | ||
283 | * 'Independent' here refers to a clock which is not | ||
284 | * controlled by its parent. | ||
285 | */ | ||
286 | printk(KERN_ERR "clock: clk_disable called on independent " | ||
287 | "clock %s which has no enable_reg\n", clk->name); | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | regval32 = __raw_readl(clk->enable_reg); | ||
292 | if (clk->flags & INVERT_ENABLE) | ||
293 | regval32 |= (1 << clk->enable_bit); | ||
294 | else | ||
295 | regval32 &= ~(1 << clk->enable_bit); | ||
296 | __raw_writel(regval32, clk->enable_reg); | ||
297 | wmb(); | ||
298 | } | ||
299 | |||
300 | void omap2_clk_disable(struct clk *clk) | ||
301 | { | ||
302 | if (clk->usecount > 0 && !(--clk->usecount)) { | ||
303 | _omap2_clk_disable(clk); | ||
304 | if (likely((u32)clk->parent)) | ||
305 | omap2_clk_disable(clk->parent); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | int omap2_clk_enable(struct clk *clk) | ||
310 | { | ||
311 | int ret = 0; | ||
312 | |||
313 | if (clk->usecount++ == 0) { | ||
314 | if (likely((u32)clk->parent)) | ||
315 | ret = omap2_clk_enable(clk->parent); | ||
316 | |||
317 | if (unlikely(ret != 0)) { | ||
318 | clk->usecount--; | ||
319 | return ret; | ||
320 | } | ||
321 | |||
322 | ret = _omap2_clk_enable(clk); | ||
323 | |||
324 | if (unlikely(ret != 0) && clk->parent) { | ||
325 | omap2_clk_disable(clk->parent); | ||
326 | clk->usecount--; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Used for clocks that are part of CLKSEL_xyz governed clocks. | ||
335 | * REVISIT: Maybe change to use clk->enable() functions like on omap1? | ||
336 | */ | ||
337 | void omap2_clksel_recalc(struct clk *clk) | ||
338 | { | ||
339 | u32 div = 0; | ||
340 | |||
341 | pr_debug("clock: recalc'ing clksel clk %s\n", clk->name); | ||
342 | |||
343 | div = omap2_clksel_get_divisor(clk); | ||
344 | if (div == 0) | ||
345 | return; | ||
346 | |||
347 | if (unlikely(clk->rate == clk->parent->rate / div)) | ||
348 | return; | ||
349 | clk->rate = clk->parent->rate / div; | ||
350 | |||
351 | pr_debug("clock: new clock rate is %ld (div %d)\n", clk->rate, div); | ||
352 | |||
353 | if (unlikely(clk->flags & RATE_PROPAGATES)) | ||
354 | propagate_rate(clk); | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * omap2_get_clksel_by_parent - return clksel struct for a given clk & parent | ||
359 | * @clk: OMAP struct clk ptr to inspect | ||
360 | * @src_clk: OMAP struct clk ptr of the parent clk to search for | ||
361 | * | ||
362 | * Scan the struct clksel array associated with the clock to find | ||
363 | * the element associated with the supplied parent clock address. | ||
364 | * Returns a pointer to the struct clksel on success or NULL on error. | ||
365 | */ | ||
366 | const struct clksel *omap2_get_clksel_by_parent(struct clk *clk, | ||
367 | struct clk *src_clk) | ||
368 | { | ||
369 | const struct clksel *clks; | ||
370 | |||
371 | if (!clk->clksel) | ||
372 | return NULL; | ||
373 | |||
374 | for (clks = clk->clksel; clks->parent; clks++) { | ||
375 | if (clks->parent == src_clk) | ||
376 | break; /* Found the requested parent */ | ||
377 | } | ||
378 | |||
379 | if (!clks->parent) { | ||
380 | printk(KERN_ERR "clock: Could not find parent clock %s in " | ||
381 | "clksel array of clock %s\n", src_clk->name, | ||
382 | clk->name); | ||
383 | return NULL; | ||
384 | } | ||
385 | |||
386 | return clks; | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * omap2_clksel_round_rate_div - find divisor for the given clock and rate | ||
391 | * @clk: OMAP struct clk to use | ||
392 | * @target_rate: desired clock rate | ||
393 | * @new_div: ptr to where we should store the divisor | ||
394 | * | ||
395 | * Finds 'best' divider value in an array based on the source and target | ||
396 | * rates. The divider array must be sorted with smallest divider first. | ||
397 | * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, | ||
398 | * they are only settable as part of virtual_prcm set. | ||
399 | * | ||
400 | * Returns the rounded clock rate or returns 0xffffffff on error. | ||
401 | */ | ||
402 | u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | ||
403 | u32 *new_div) | ||
404 | { | ||
405 | unsigned long test_rate; | ||
406 | const struct clksel *clks; | ||
407 | const struct clksel_rate *clkr; | ||
408 | u32 last_div = 0; | ||
409 | |||
410 | printk(KERN_INFO "clock: clksel_round_rate_div: %s target_rate %ld\n", | ||
411 | clk->name, target_rate); | ||
412 | |||
413 | *new_div = 1; | ||
414 | |||
415 | clks = omap2_get_clksel_by_parent(clk, clk->parent); | ||
416 | if (clks == NULL) | ||
417 | return ~0; | ||
418 | |||
419 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
420 | if (!(clkr->flags & cpu_mask)) | ||
421 | continue; | ||
422 | |||
423 | /* Sanity check */ | ||
424 | if (clkr->div <= last_div) | ||
425 | printk(KERN_ERR "clock: clksel_rate table not sorted " | ||
426 | "for clock %s", clk->name); | ||
427 | |||
428 | last_div = clkr->div; | ||
429 | |||
430 | test_rate = clk->parent->rate / clkr->div; | ||
431 | |||
432 | if (test_rate <= target_rate) | ||
433 | break; /* found it */ | ||
434 | } | ||
435 | |||
436 | if (!clkr->div) { | ||
437 | printk(KERN_ERR "clock: Could not find divisor for target " | ||
438 | "rate %ld for clock %s parent %s\n", target_rate, | ||
439 | clk->name, clk->parent->name); | ||
440 | return ~0; | ||
441 | } | ||
442 | |||
443 | *new_div = clkr->div; | ||
444 | |||
445 | printk(KERN_INFO "clock: new_div = %d, new_rate = %ld\n", *new_div, | ||
446 | (clk->parent->rate / clkr->div)); | ||
447 | |||
448 | return (clk->parent->rate / clkr->div); | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * omap2_clksel_round_rate - find rounded rate for the given clock and rate | ||
453 | * @clk: OMAP struct clk to use | ||
454 | * @target_rate: desired clock rate | ||
455 | * | ||
456 | * Compatibility wrapper for OMAP clock framework | ||
457 | * Finds best target rate based on the source clock and possible dividers. | ||
458 | * rates. The divider array must be sorted with smallest divider first. | ||
459 | * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, | ||
460 | * they are only settable as part of virtual_prcm set. | ||
461 | * | ||
462 | * Returns the rounded clock rate or returns 0xffffffff on error. | ||
463 | */ | ||
464 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate) | ||
465 | { | ||
466 | u32 new_div; | ||
467 | |||
468 | return omap2_clksel_round_rate_div(clk, target_rate, &new_div); | ||
469 | } | ||
470 | |||
471 | |||
472 | /* Given a clock and a rate apply a clock specific rounding function */ | ||
473 | long omap2_clk_round_rate(struct clk *clk, unsigned long rate) | ||
474 | { | ||
475 | if (clk->round_rate != 0) | ||
476 | return clk->round_rate(clk, rate); | ||
477 | |||
478 | if (clk->flags & RATE_FIXED) | ||
479 | printk(KERN_ERR "clock: generic omap2_clk_round_rate called " | ||
480 | "on fixed-rate clock %s\n", clk->name); | ||
481 | |||
482 | return clk->rate; | ||
483 | } | ||
484 | |||
485 | /** | ||
486 | * omap2_clksel_to_divisor() - turn clksel field value into integer divider | ||
487 | * @clk: OMAP struct clk to use | ||
488 | * @field_val: register field value to find | ||
489 | * | ||
490 | * Given a struct clk of a rate-selectable clksel clock, and a register field | ||
491 | * value to search for, find the corresponding clock divisor. The register | ||
492 | * field value should be pre-masked and shifted down so the LSB is at bit 0 | ||
493 | * before calling. Returns 0 on error | ||
494 | */ | ||
495 | u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val) | ||
496 | { | ||
497 | const struct clksel *clks; | ||
498 | const struct clksel_rate *clkr; | ||
499 | |||
500 | clks = omap2_get_clksel_by_parent(clk, clk->parent); | ||
501 | if (clks == NULL) | ||
502 | return 0; | ||
503 | |||
504 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
505 | if ((clkr->flags & cpu_mask) && (clkr->val == field_val)) | ||
506 | break; | ||
507 | } | ||
508 | |||
509 | if (!clkr->div) { | ||
510 | printk(KERN_ERR "clock: Could not find fieldval %d for " | ||
511 | "clock %s parent %s\n", field_val, clk->name, | ||
512 | clk->parent->name); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | return clkr->div; | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value | ||
521 | * @clk: OMAP struct clk to use | ||
522 | * @div: integer divisor to search for | ||
523 | * | ||
524 | * Given a struct clk of a rate-selectable clksel clock, and a clock divisor, | ||
525 | * find the corresponding register field value. The return register value is | ||
526 | * the value before left-shifting. Returns 0xffffffff on error | ||
527 | */ | ||
528 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div) | ||
529 | { | ||
530 | const struct clksel *clks; | ||
531 | const struct clksel_rate *clkr; | ||
532 | |||
533 | /* should never happen */ | ||
534 | WARN_ON(div == 0); | ||
535 | |||
536 | clks = omap2_get_clksel_by_parent(clk, clk->parent); | ||
537 | if (clks == NULL) | ||
538 | return 0; | ||
539 | |||
540 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
541 | if ((clkr->flags & cpu_mask) && (clkr->div == div)) | ||
542 | break; | ||
543 | } | ||
544 | |||
545 | if (!clkr->div) { | ||
546 | printk(KERN_ERR "clock: Could not find divisor %d for " | ||
547 | "clock %s parent %s\n", div, clk->name, | ||
548 | clk->parent->name); | ||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | return clkr->val; | ||
553 | } | ||
554 | |||
555 | /** | ||
556 | * omap2_get_clksel - find clksel register addr & field mask for a clk | ||
557 | * @clk: struct clk to use | ||
558 | * @field_mask: ptr to u32 to store the register field mask | ||
559 | * | ||
560 | * Returns the address of the clksel register upon success or NULL on error. | ||
561 | */ | ||
562 | void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask) | ||
563 | { | ||
564 | if (unlikely((clk->clksel_reg == 0) || (clk->clksel_mask == 0))) | ||
565 | return NULL; | ||
566 | |||
567 | *field_mask = clk->clksel_mask; | ||
568 | |||
569 | return clk->clksel_reg; | ||
570 | } | ||
571 | |||
572 | /** | ||
573 | * omap2_clksel_get_divisor - get current divider applied to parent clock. | ||
574 | * @clk: OMAP struct clk to use. | ||
575 | * | ||
576 | * Returns the integer divisor upon success or 0 on error. | ||
577 | */ | ||
578 | u32 omap2_clksel_get_divisor(struct clk *clk) | ||
579 | { | ||
580 | u32 field_mask, field_val; | ||
581 | void __iomem *div_addr; | ||
582 | |||
583 | div_addr = omap2_get_clksel(clk, &field_mask); | ||
584 | if (div_addr == 0) | ||
585 | return 0; | ||
586 | |||
587 | field_val = __raw_readl(div_addr) & field_mask; | ||
588 | field_val >>= __ffs(field_mask); | ||
589 | |||
590 | return omap2_clksel_to_divisor(clk, field_val); | ||
591 | } | ||
592 | |||
593 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) | ||
594 | { | ||
595 | u32 field_mask, field_val, reg_val, validrate, new_div = 0; | ||
596 | void __iomem *div_addr; | ||
597 | |||
598 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); | ||
599 | if (validrate != rate) | ||
600 | return -EINVAL; | ||
601 | |||
602 | div_addr = omap2_get_clksel(clk, &field_mask); | ||
603 | if (div_addr == 0) | ||
604 | return -EINVAL; | ||
605 | |||
606 | field_val = omap2_divisor_to_clksel(clk, new_div); | ||
607 | if (field_val == ~0) | ||
608 | return -EINVAL; | ||
609 | |||
610 | reg_val = __raw_readl(div_addr); | ||
611 | reg_val &= ~field_mask; | ||
612 | reg_val |= (field_val << __ffs(field_mask)); | ||
613 | __raw_writel(reg_val, div_addr); | ||
614 | wmb(); | ||
615 | |||
616 | clk->rate = clk->parent->rate / new_div; | ||
617 | |||
618 | if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) { | ||
619 | __raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL); | ||
620 | wmb(); | ||
621 | } | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | |||
627 | /* Set the clock rate for a clock source */ | ||
628 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate) | ||
629 | { | ||
630 | int ret = -EINVAL; | ||
631 | |||
632 | pr_debug("clock: set_rate for clock %s to rate %ld\n", clk->name, rate); | ||
633 | |||
634 | /* CONFIG_PARTICIPANT clocks are changed only in sets via the | ||
635 | rate table mechanism, driven by mpu_speed */ | ||
636 | if (clk->flags & CONFIG_PARTICIPANT) | ||
637 | return -EINVAL; | ||
638 | |||
639 | /* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */ | ||
640 | if (clk->set_rate != 0) | ||
641 | ret = clk->set_rate(clk, rate); | ||
642 | |||
643 | if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) | ||
644 | propagate_rate(clk); | ||
645 | |||
646 | return ret; | ||
647 | } | ||
648 | |||
649 | /* | ||
650 | * Converts encoded control register address into a full address | ||
651 | * On error, *src_addr will be returned as 0. | ||
652 | */ | ||
653 | static u32 omap2_clksel_get_src_field(void __iomem **src_addr, | ||
654 | struct clk *src_clk, u32 *field_mask, | ||
655 | struct clk *clk, u32 *parent_div) | ||
656 | { | ||
657 | const struct clksel *clks; | ||
658 | const struct clksel_rate *clkr; | ||
659 | |||
660 | *parent_div = 0; | ||
661 | *src_addr = 0; | ||
662 | |||
663 | clks = omap2_get_clksel_by_parent(clk, src_clk); | ||
664 | if (clks == NULL) | ||
665 | return 0; | ||
666 | |||
667 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
668 | if (clkr->flags & (cpu_mask | DEFAULT_RATE)) | ||
669 | break; /* Found the default rate for this platform */ | ||
670 | } | ||
671 | |||
672 | if (!clkr->div) { | ||
673 | printk(KERN_ERR "clock: Could not find default rate for " | ||
674 | "clock %s parent %s\n", clk->name, | ||
675 | src_clk->parent->name); | ||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | /* Should never happen. Add a clksel mask to the struct clk. */ | ||
680 | WARN_ON(clk->clksel_mask == 0); | ||
681 | |||
682 | *field_mask = clk->clksel_mask; | ||
683 | *src_addr = clk->clksel_reg; | ||
684 | *parent_div = clkr->div; | ||
685 | |||
686 | return clkr->val; | ||
687 | } | ||
688 | |||
689 | int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) | ||
690 | { | ||
691 | void __iomem *src_addr; | ||
692 | u32 field_val, field_mask, reg_val, parent_div; | ||
693 | |||
694 | if (unlikely(clk->flags & CONFIG_PARTICIPANT)) | ||
695 | return -EINVAL; | ||
696 | |||
697 | if (!clk->clksel) | ||
698 | return -EINVAL; | ||
699 | |||
700 | field_val = omap2_clksel_get_src_field(&src_addr, new_parent, | ||
701 | &field_mask, clk, &parent_div); | ||
702 | if (src_addr == 0) | ||
703 | return -EINVAL; | ||
704 | |||
705 | if (clk->usecount > 0) | ||
706 | _omap2_clk_disable(clk); | ||
707 | |||
708 | /* Set new source value (previous dividers if any in effect) */ | ||
709 | reg_val = __raw_readl(src_addr) & ~field_mask; | ||
710 | reg_val |= (field_val << __ffs(field_mask)); | ||
711 | __raw_writel(reg_val, src_addr); | ||
712 | wmb(); | ||
713 | |||
714 | if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) { | ||
715 | __raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL); | ||
716 | wmb(); | ||
717 | } | ||
718 | |||
719 | if (clk->usecount > 0) | ||
720 | _omap2_clk_enable(clk); | ||
721 | |||
722 | clk->parent = new_parent; | ||
723 | |||
724 | /* CLKSEL clocks follow their parents' rates, divided by a divisor */ | ||
725 | clk->rate = new_parent->rate; | ||
726 | |||
727 | if (parent_div > 0) | ||
728 | clk->rate /= parent_div; | ||
729 | |||
730 | pr_debug("clock: set parent of %s to %s (new rate %ld)\n", | ||
731 | clk->name, clk->parent->name, clk->rate); | ||
732 | |||
733 | if (unlikely(clk->flags & RATE_PROPAGATES)) | ||
734 | propagate_rate(clk); | ||
735 | |||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | /*------------------------------------------------------------------------- | ||
740 | * Omap2 clock reset and init functions | ||
741 | *-------------------------------------------------------------------------*/ | ||
742 | |||
743 | #ifdef CONFIG_OMAP_RESET_CLOCKS | ||
744 | void omap2_clk_disable_unused(struct clk *clk) | ||
745 | { | ||
746 | u32 regval32, v; | ||
747 | |||
748 | v = (clk->flags & INVERT_ENABLE) ? (1 << clk->enable_bit) : 0; | ||
749 | |||
750 | regval32 = __raw_readl(clk->enable_reg); | ||
751 | if ((regval32 & (1 << clk->enable_bit)) == v) | ||
752 | return; | ||
753 | |||
754 | printk(KERN_INFO "Disabling unused clock \"%s\"\n", clk->name); | ||
755 | _omap2_clk_disable(clk); | ||
756 | } | ||
757 | #endif | ||
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h new file mode 100644 index 000000000000..d9cc99c466da --- /dev/null +++ b/arch/arm/mach-omap2/clock.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap2/clock.h | ||
3 | * | ||
4 | * Copyright (C) 2005 Texas Instruments Inc. | ||
5 | * Richard Woodruff <r-woodruff2@ti.com> | ||
6 | * Created for OMAP2. | ||
7 | * | ||
8 | * Copyright (C) 2004 Nokia corporation | ||
9 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
10 | * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc | ||
11 | * | ||
12 | * Copyright (C) 2007 Texas Instruments, Inc. | ||
13 | * Copyright (C) 2007 Nokia Corporation | ||
14 | * Paul Walmsley | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License version 2 as | ||
18 | * published by the Free Software Foundation. | ||
19 | */ | ||
20 | |||
21 | #ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H | ||
22 | #define __ARCH_ARM_MACH_OMAP2_CLOCK_H | ||
23 | |||
24 | #include <asm/arch/clock.h> | ||
25 | |||
26 | int omap2_clk_enable(struct clk *clk); | ||
27 | void omap2_clk_disable(struct clk *clk); | ||
28 | long omap2_clk_round_rate(struct clk *clk, unsigned long rate); | ||
29 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate); | ||
30 | int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent); | ||
31 | |||
32 | #ifdef CONFIG_OMAP_RESET_CLOCKS | ||
33 | void omap2_clk_disable_unused(struct clk *clk); | ||
34 | #else | ||
35 | #define omap2_clk_disable_unused NULL | ||
36 | #endif | ||
37 | |||
38 | void omap2_clksel_recalc(struct clk *clk); | ||
39 | void omap2_init_clksel_parent(struct clk *clk); | ||
40 | u32 omap2_clksel_get_divisor(struct clk *clk); | ||
41 | u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | ||
42 | u32 *new_div); | ||
43 | u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val); | ||
44 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div); | ||
45 | void omap2_fixed_divisor_recalc(struct clk *clk); | ||
46 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate); | ||
47 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate); | ||
48 | u32 omap2_get_dpll_rate(struct clk *clk); | ||
49 | int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name); | ||
50 | |||
51 | extern u8 cpu_mask; | ||
52 | |||
53 | /* clksel_rate data common to 24xx/343x */ | ||
54 | static const struct clksel_rate gpt_32k_rates[] = { | ||
55 | { .div = 1, .val = 0, .flags = RATE_IN_24XX | RATE_IN_343X | DEFAULT_RATE }, | ||
56 | { .div = 0 } | ||
57 | }; | ||
58 | |||
59 | static const struct clksel_rate gpt_sys_rates[] = { | ||
60 | { .div = 1, .val = 1, .flags = RATE_IN_24XX | RATE_IN_343X | DEFAULT_RATE }, | ||
61 | { .div = 0 } | ||
62 | }; | ||
63 | |||
64 | static const struct clksel_rate gfx_l3_rates[] = { | ||
65 | { .div = 1, .val = 1, .flags = RATE_IN_24XX | RATE_IN_343X }, | ||
66 | { .div = 2, .val = 2, .flags = RATE_IN_24XX | RATE_IN_343X | DEFAULT_RATE }, | ||
67 | { .div = 3, .val = 3, .flags = RATE_IN_243X | RATE_IN_343X }, | ||
68 | { .div = 4, .val = 4, .flags = RATE_IN_243X | RATE_IN_343X }, | ||
69 | { .div = 0 } | ||
70 | }; | ||
71 | |||
72 | |||
73 | #endif | ||