Commit | Line | Data |
---|---|---|
543d9378 PW |
1 | /* |
2 | * linux/arch/arm/mach-omap2/clock.c | |
3 | * | |
a16e9703 TL |
4 | * Copyright (C) 2005-2008 Texas Instruments, Inc. |
5 | * Copyright (C) 2004-2008 Nokia Corporation | |
543d9378 | 6 | * |
a16e9703 TL |
7 | * Contacts: |
8 | * Richard Woodruff <r-woodruff2@ti.com> | |
543d9378 PW |
9 | * Paul Walmsley |
10 | * | |
543d9378 PW |
11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2 as | |
13 | * published by the Free Software Foundation. | |
14 | */ | |
15 | #undef DEBUG | |
16 | ||
17 | #include <linux/module.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/device.h> | |
20 | #include <linux/list.h> | |
21 | #include <linux/errno.h> | |
22 | #include <linux/delay.h> | |
23 | #include <linux/clk.h> | |
fced80c7 | 24 | #include <linux/io.h> |
fbd3bdb2 | 25 | #include <linux/bitops.h> |
543d9378 | 26 | |
ce491cf8 TL |
27 | #include <plat/clock.h> |
28 | #include <plat/clockdomain.h> | |
29 | #include <plat/cpu.h> | |
30 | #include <plat/prcm.h> | |
543d9378 | 31 | |
543d9378 PW |
32 | #include "clock.h" |
33 | #include "prm.h" | |
34 | #include "prm-regbits-24xx.h" | |
35 | #include "cm.h" | |
36 | #include "cm-regbits-24xx.h" | |
37 | #include "cm-regbits-34xx.h" | |
38 | ||
543d9378 PW |
39 | u8 cpu_mask; |
40 | ||
41 | /*------------------------------------------------------------------------- | |
911bd739 | 42 | * OMAP2/3/4 specific clock functions |
543d9378 PW |
43 | *-------------------------------------------------------------------------*/ |
44 | ||
439764cc PW |
45 | /** |
46 | * _omap2xxx_clk_commit - commit clock parent/rate changes in hardware | |
47 | * @clk: struct clk * | |
48 | * | |
49 | * If @clk has the DELAYED_APP flag set, meaning that parent/rate changes | |
50 | * don't take effect until the VALID_CONFIG bit is written, write the | |
51 | * VALID_CONFIG bit and wait for the write to complete. No return value. | |
52 | */ | |
53 | static void _omap2xxx_clk_commit(struct clk *clk) | |
54 | { | |
55 | if (!cpu_is_omap24xx()) | |
56 | return; | |
57 | ||
58 | if (!(clk->flags & DELAYED_APP)) | |
59 | return; | |
60 | ||
61 | prm_write_mod_reg(OMAP24XX_VALID_CONFIG, OMAP24XX_GR_MOD, | |
8e3bd351 | 62 | OMAP2_PRCM_CLKCFG_CTRL_OFFSET); |
439764cc | 63 | /* OCP barrier */ |
8e3bd351 | 64 | prm_read_mod_reg(OMAP24XX_GR_MOD, OMAP2_PRCM_CLKCFG_CTRL_OFFSET); |
439764cc PW |
65 | } |
66 | ||
333943ba PW |
67 | /** |
68 | * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk | |
69 | * @clk: OMAP clock struct ptr to use | |
70 | * | |
71 | * Convert a clockdomain name stored in a struct clk 'clk' into a | |
72 | * clockdomain pointer, and save it into the struct clk. Intended to be | |
73 | * called during clk_register(). No return value. | |
74 | */ | |
75 | void omap2_init_clk_clkdm(struct clk *clk) | |
76 | { | |
77 | struct clockdomain *clkdm; | |
78 | ||
79 | if (!clk->clkdm_name) | |
80 | return; | |
81 | ||
82 | clkdm = clkdm_lookup(clk->clkdm_name); | |
83 | if (clkdm) { | |
84 | pr_debug("clock: associated clk %s to clkdm %s\n", | |
85 | clk->name, clk->clkdm_name); | |
86 | clk->clkdm = clkdm; | |
87 | } else { | |
88 | pr_debug("clock: could not associate clk %s to " | |
89 | "clkdm %s\n", clk->name, clk->clkdm_name); | |
90 | } | |
91 | } | |
92 | ||
543d9378 PW |
93 | /** |
94 | * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware | |
95 | * @clk: OMAP clock struct ptr to use | |
96 | * | |
97 | * Given a pointer to a source-selectable struct clk, read the hardware | |
98 | * register and determine what its parent is currently set to. Update the | |
99 | * clk->parent field with the appropriate clk ptr. | |
100 | */ | |
101 | void omap2_init_clksel_parent(struct clk *clk) | |
102 | { | |
103 | const struct clksel *clks; | |
104 | const struct clksel_rate *clkr; | |
105 | u32 r, found = 0; | |
106 | ||
107 | if (!clk->clksel) | |
108 | return; | |
109 | ||
110 | r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | |
111 | r >>= __ffs(clk->clksel_mask); | |
112 | ||
113 | for (clks = clk->clksel; clks->parent && !found; clks++) { | |
114 | for (clkr = clks->rates; clkr->div && !found; clkr++) { | |
115 | if ((clkr->flags & cpu_mask) && (clkr->val == r)) { | |
116 | if (clk->parent != clks->parent) { | |
117 | pr_debug("clock: inited %s parent " | |
118 | "to %s (was %s)\n", | |
119 | clk->name, clks->parent->name, | |
120 | ((clk->parent) ? | |
121 | clk->parent->name : "NULL")); | |
3f0a820c | 122 | clk_reparent(clk, clks->parent); |
543d9378 PW |
123 | }; |
124 | found = 1; | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | if (!found) | |
130 | printk(KERN_ERR "clock: init parent: could not find " | |
131 | "regval %0x for clock %s\n", r, clk->name); | |
132 | ||
133 | return; | |
134 | } | |
135 | ||
543d9378 | 136 | /** |
72350b29 PW |
137 | * omap2_clk_dflt_find_companion - find companion clock to @clk |
138 | * @clk: struct clk * to find the companion clock of | |
139 | * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in | |
140 | * @other_bit: u8 ** to return the companion clock bit shift in | |
141 | * | |
142 | * Note: We don't need special code here for INVERT_ENABLE for the | |
143 | * time being since INVERT_ENABLE only applies to clocks enabled by | |
144 | * CM_CLKEN_PLL | |
543d9378 | 145 | * |
72350b29 PW |
146 | * Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes it's |
147 | * just a matter of XORing the bits. | |
148 | * | |
149 | * Some clocks don't have companion clocks. For example, modules with | |
150 | * only an interface clock (such as MAILBOXES) don't have a companion | |
151 | * clock. Right now, this code relies on the hardware exporting a bit | |
152 | * in the correct companion register that indicates that the | |
153 | * nonexistent 'companion clock' is active. Future patches will | |
154 | * associate this type of code with per-module data structures to | |
155 | * avoid this issue, and remove the casts. No return value. | |
543d9378 | 156 | */ |
72350b29 PW |
157 | void omap2_clk_dflt_find_companion(struct clk *clk, void __iomem **other_reg, |
158 | u8 *other_bit) | |
543d9378 | 159 | { |
72350b29 | 160 | u32 r; |
543d9378 PW |
161 | |
162 | /* | |
72350b29 PW |
163 | * Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes |
164 | * it's just a matter of XORing the bits. | |
543d9378 | 165 | */ |
72350b29 | 166 | r = ((__force u32)clk->enable_reg ^ (CM_FCLKEN ^ CM_ICLKEN)); |
543d9378 | 167 | |
72350b29 PW |
168 | *other_reg = (__force void __iomem *)r; |
169 | *other_bit = clk->enable_bit; | |
170 | } | |
543d9378 | 171 | |
72350b29 PW |
172 | /** |
173 | * omap2_clk_dflt_find_idlest - find CM_IDLEST reg va, bit shift for @clk | |
174 | * @clk: struct clk * to find IDLEST info for | |
175 | * @idlest_reg: void __iomem ** to return the CM_IDLEST va in | |
176 | * @idlest_bit: u8 ** to return the CM_IDLEST bit shift in | |
177 | * | |
178 | * Return the CM_IDLEST register address and bit shift corresponding | |
179 | * to the module that "owns" this clock. This default code assumes | |
180 | * that the CM_IDLEST bit shift is the CM_*CLKEN bit shift, and that | |
181 | * the IDLEST register address ID corresponds to the CM_*CLKEN | |
182 | * register address ID (e.g., that CM_FCLKEN2 corresponds to | |
183 | * CM_IDLEST2). This is not true for all modules. No return value. | |
543d9378 | 184 | */ |
72350b29 PW |
185 | void omap2_clk_dflt_find_idlest(struct clk *clk, void __iomem **idlest_reg, |
186 | u8 *idlest_bit) | |
543d9378 | 187 | { |
72350b29 | 188 | u32 r; |
543d9378 | 189 | |
72350b29 PW |
190 | r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20); |
191 | *idlest_reg = (__force void __iomem *)r; | |
192 | *idlest_bit = clk->enable_bit; | |
193 | } | |
543d9378 | 194 | |
72350b29 PW |
195 | /** |
196 | * omap2_module_wait_ready - wait for an OMAP module to leave IDLE | |
197 | * @clk: struct clk * belonging to the module | |
198 | * | |
199 | * If the necessary clocks for the OMAP hardware IP block that | |
200 | * corresponds to clock @clk are enabled, then wait for the module to | |
201 | * indicate readiness (i.e., to leave IDLE). This code does not | |
202 | * belong in the clock code and will be moved in the medium term to | |
203 | * module-dependent code. No return value. | |
204 | */ | |
205 | static void omap2_module_wait_ready(struct clk *clk) | |
206 | { | |
207 | void __iomem *companion_reg, *idlest_reg; | |
208 | u8 other_bit, idlest_bit; | |
209 | ||
210 | /* Not all modules have multiple clocks that their IDLEST depends on */ | |
211 | if (clk->ops->find_companion) { | |
212 | clk->ops->find_companion(clk, &companion_reg, &other_bit); | |
213 | if (!(__raw_readl(companion_reg) & (1 << other_bit))) | |
214 | return; | |
215 | } | |
543d9378 | 216 | |
72350b29 | 217 | clk->ops->find_idlest(clk, &idlest_reg, &idlest_bit); |
543d9378 | 218 | |
72350b29 | 219 | omap2_cm_wait_idlest(idlest_reg, (1 << idlest_bit), clk->name); |
543d9378 PW |
220 | } |
221 | ||
72350b29 | 222 | int omap2_dflt_clk_enable(struct clk *clk) |
543d9378 | 223 | { |
ee1eec36 | 224 | u32 v; |
543d9378 | 225 | |
c0fc18c5 | 226 | if (unlikely(clk->enable_reg == NULL)) { |
72350b29 | 227 | pr_err("clock.c: Enable for %s without enable code\n", |
543d9378 PW |
228 | clk->name); |
229 | return 0; /* REVISIT: -EINVAL */ | |
230 | } | |
231 | ||
ee1eec36 | 232 | v = __raw_readl(clk->enable_reg); |
543d9378 | 233 | if (clk->flags & INVERT_ENABLE) |
ee1eec36 | 234 | v &= ~(1 << clk->enable_bit); |
543d9378 | 235 | else |
ee1eec36 PW |
236 | v |= (1 << clk->enable_bit); |
237 | __raw_writel(v, clk->enable_reg); | |
f11fda6a | 238 | v = __raw_readl(clk->enable_reg); /* OCP barrier */ |
543d9378 | 239 | |
72350b29 PW |
240 | if (clk->ops->find_idlest) |
241 | omap2_module_wait_ready(clk); | |
543d9378 | 242 | |
72350b29 | 243 | return 0; |
bc51da4e RK |
244 | } |
245 | ||
72350b29 | 246 | void omap2_dflt_clk_disable(struct clk *clk) |
543d9378 | 247 | { |
ee1eec36 | 248 | u32 v; |
543d9378 | 249 | |
fecb494b | 250 | if (!clk->enable_reg) { |
543d9378 PW |
251 | /* |
252 | * 'Independent' here refers to a clock which is not | |
253 | * controlled by its parent. | |
254 | */ | |
255 | printk(KERN_ERR "clock: clk_disable called on independent " | |
256 | "clock %s which has no enable_reg\n", clk->name); | |
257 | return; | |
258 | } | |
259 | ||
ee1eec36 | 260 | v = __raw_readl(clk->enable_reg); |
543d9378 | 261 | if (clk->flags & INVERT_ENABLE) |
ee1eec36 | 262 | v |= (1 << clk->enable_bit); |
543d9378 | 263 | else |
ee1eec36 PW |
264 | v &= ~(1 << clk->enable_bit); |
265 | __raw_writel(v, clk->enable_reg); | |
de07fedd | 266 | /* No OCP barrier needed here since it is a disable operation */ |
543d9378 PW |
267 | } |
268 | ||
b36ee724 | 269 | const struct clkops clkops_omap2_dflt_wait = { |
72350b29 | 270 | .enable = omap2_dflt_clk_enable, |
b36ee724 | 271 | .disable = omap2_dflt_clk_disable, |
72350b29 PW |
272 | .find_companion = omap2_clk_dflt_find_companion, |
273 | .find_idlest = omap2_clk_dflt_find_idlest, | |
b36ee724 RK |
274 | }; |
275 | ||
bc51da4e RK |
276 | const struct clkops clkops_omap2_dflt = { |
277 | .enable = omap2_dflt_clk_enable, | |
278 | .disable = omap2_dflt_clk_disable, | |
279 | }; | |
280 | ||
b36ee724 RK |
281 | /* Enables clock without considering parent dependencies or use count |
282 | * REVISIT: Maybe change this to use clk->enable like on omap1? | |
283 | */ | |
284 | static int _omap2_clk_enable(struct clk *clk) | |
285 | { | |
286 | return clk->ops->enable(clk); | |
287 | } | |
288 | ||
289 | /* Disables clock without considering parent dependencies or use count */ | |
290 | static void _omap2_clk_disable(struct clk *clk) | |
291 | { | |
292 | clk->ops->disable(clk); | |
543d9378 PW |
293 | } |
294 | ||
295 | void omap2_clk_disable(struct clk *clk) | |
296 | { | |
297 | if (clk->usecount > 0 && !(--clk->usecount)) { | |
298 | _omap2_clk_disable(clk); | |
fecb494b | 299 | if (clk->parent) |
543d9378 | 300 | omap2_clk_disable(clk->parent); |
333943ba PW |
301 | if (clk->clkdm) |
302 | omap2_clkdm_clk_disable(clk->clkdm, clk); | |
303 | ||
543d9378 PW |
304 | } |
305 | } | |
306 | ||
307 | int omap2_clk_enable(struct clk *clk) | |
308 | { | |
309 | int ret = 0; | |
310 | ||
311 | if (clk->usecount++ == 0) { | |
333943ba PW |
312 | if (clk->clkdm) |
313 | omap2_clkdm_clk_enable(clk->clkdm, clk); | |
314 | ||
a7f8c599 | 315 | if (clk->parent) { |
543d9378 | 316 | ret = omap2_clk_enable(clk->parent); |
a7f8c599 RK |
317 | if (ret) |
318 | goto err; | |
543d9378 | 319 | } |
333943ba | 320 | |
543d9378 | 321 | ret = _omap2_clk_enable(clk); |
a7f8c599 | 322 | if (ret) { |
a7f8c599 | 323 | if (clk->parent) |
333943ba | 324 | omap2_clk_disable(clk->parent); |
a7f8c599 RK |
325 | |
326 | goto err; | |
543d9378 PW |
327 | } |
328 | } | |
a7f8c599 | 329 | return ret; |
543d9378 | 330 | |
a7f8c599 | 331 | err: |
8263e5b3 RK |
332 | if (clk->clkdm) |
333 | omap2_clkdm_clk_disable(clk->clkdm, clk); | |
a7f8c599 | 334 | clk->usecount--; |
543d9378 PW |
335 | return ret; |
336 | } | |
337 | ||
338 | /* | |
339 | * Used for clocks that are part of CLKSEL_xyz governed clocks. | |
340 | * REVISIT: Maybe change to use clk->enable() functions like on omap1? | |
341 | */ | |
8b9dbc16 | 342 | unsigned long omap2_clksel_recalc(struct clk *clk) |
543d9378 | 343 | { |
8b9dbc16 | 344 | unsigned long rate; |
543d9378 PW |
345 | u32 div = 0; |
346 | ||
347 | pr_debug("clock: recalc'ing clksel clk %s\n", clk->name); | |
348 | ||
349 | div = omap2_clksel_get_divisor(clk); | |
350 | if (div == 0) | |
8b9dbc16 | 351 | return clk->rate; |
543d9378 | 352 | |
8b9dbc16 | 353 | rate = clk->parent->rate / div; |
543d9378 | 354 | |
8b9dbc16 | 355 | pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div); |
543d9378 | 356 | |
8b9dbc16 | 357 | return rate; |
543d9378 PW |
358 | } |
359 | ||
360 | /** | |
361 | * omap2_get_clksel_by_parent - return clksel struct for a given clk & parent | |
362 | * @clk: OMAP struct clk ptr to inspect | |
363 | * @src_clk: OMAP struct clk ptr of the parent clk to search for | |
364 | * | |
365 | * Scan the struct clksel array associated with the clock to find | |
366 | * the element associated with the supplied parent clock address. | |
367 | * Returns a pointer to the struct clksel on success or NULL on error. | |
368 | */ | |
fecb494b PW |
369 | static const struct clksel *omap2_get_clksel_by_parent(struct clk *clk, |
370 | struct clk *src_clk) | |
543d9378 PW |
371 | { |
372 | const struct clksel *clks; | |
373 | ||
374 | if (!clk->clksel) | |
375 | return NULL; | |
376 | ||
377 | for (clks = clk->clksel; clks->parent; clks++) { | |
378 | if (clks->parent == src_clk) | |
379 | break; /* Found the requested parent */ | |
380 | } | |
381 | ||
382 | if (!clks->parent) { | |
383 | printk(KERN_ERR "clock: Could not find parent clock %s in " | |
384 | "clksel array of clock %s\n", src_clk->name, | |
385 | clk->name); | |
386 | return NULL; | |
387 | } | |
388 | ||
389 | return clks; | |
390 | } | |
391 | ||
392 | /** | |
393 | * omap2_clksel_round_rate_div - find divisor for the given clock and rate | |
394 | * @clk: OMAP struct clk to use | |
395 | * @target_rate: desired clock rate | |
396 | * @new_div: ptr to where we should store the divisor | |
397 | * | |
398 | * Finds 'best' divider value in an array based on the source and target | |
399 | * rates. The divider array must be sorted with smallest divider first. | |
400 | * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, | |
401 | * they are only settable as part of virtual_prcm set. | |
402 | * | |
403 | * Returns the rounded clock rate or returns 0xffffffff on error. | |
404 | */ | |
405 | u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | |
406 | u32 *new_div) | |
407 | { | |
408 | unsigned long test_rate; | |
409 | const struct clksel *clks; | |
410 | const struct clksel_rate *clkr; | |
411 | u32 last_div = 0; | |
412 | ||
b7aee4bf PW |
413 | pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n", |
414 | clk->name, target_rate); | |
543d9378 PW |
415 | |
416 | *new_div = 1; | |
417 | ||
418 | clks = omap2_get_clksel_by_parent(clk, clk->parent); | |
fecb494b | 419 | if (!clks) |
543d9378 PW |
420 | return ~0; |
421 | ||
422 | for (clkr = clks->rates; clkr->div; clkr++) { | |
423 | if (!(clkr->flags & cpu_mask)) | |
424 | continue; | |
425 | ||
426 | /* Sanity check */ | |
427 | if (clkr->div <= last_div) | |
b7aee4bf | 428 | pr_err("clock: clksel_rate table not sorted " |
543d9378 PW |
429 | "for clock %s", clk->name); |
430 | ||
431 | last_div = clkr->div; | |
432 | ||
433 | test_rate = clk->parent->rate / clkr->div; | |
434 | ||
435 | if (test_rate <= target_rate) | |
436 | break; /* found it */ | |
437 | } | |
438 | ||
439 | if (!clkr->div) { | |
b7aee4bf | 440 | pr_err("clock: Could not find divisor for target " |
543d9378 PW |
441 | "rate %ld for clock %s parent %s\n", target_rate, |
442 | clk->name, clk->parent->name); | |
443 | return ~0; | |
444 | } | |
445 | ||
446 | *new_div = clkr->div; | |
447 | ||
b7aee4bf PW |
448 | pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div, |
449 | (clk->parent->rate / clkr->div)); | |
543d9378 PW |
450 | |
451 | return (clk->parent->rate / clkr->div); | |
452 | } | |
453 | ||
454 | /** | |
455 | * omap2_clksel_round_rate - find rounded rate for the given clock and rate | |
456 | * @clk: OMAP struct clk to use | |
457 | * @target_rate: desired clock rate | |
458 | * | |
459 | * Compatibility wrapper for OMAP clock framework | |
460 | * Finds best target rate based on the source clock and possible dividers. | |
461 | * rates. The divider array must be sorted with smallest divider first. | |
462 | * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, | |
463 | * they are only settable as part of virtual_prcm set. | |
464 | * | |
465 | * Returns the rounded clock rate or returns 0xffffffff on error. | |
466 | */ | |
467 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate) | |
468 | { | |
469 | u32 new_div; | |
470 | ||
471 | return omap2_clksel_round_rate_div(clk, target_rate, &new_div); | |
472 | } | |
473 | ||
474 | ||
475 | /* Given a clock and a rate apply a clock specific rounding function */ | |
476 | long omap2_clk_round_rate(struct clk *clk, unsigned long rate) | |
477 | { | |
fecb494b | 478 | if (clk->round_rate) |
543d9378 PW |
479 | return clk->round_rate(clk, rate); |
480 | ||
481 | if (clk->flags & RATE_FIXED) | |
482 | printk(KERN_ERR "clock: generic omap2_clk_round_rate called " | |
483 | "on fixed-rate clock %s\n", clk->name); | |
484 | ||
485 | return clk->rate; | |
486 | } | |
487 | ||
488 | /** | |
489 | * omap2_clksel_to_divisor() - turn clksel field value into integer divider | |
490 | * @clk: OMAP struct clk to use | |
491 | * @field_val: register field value to find | |
492 | * | |
493 | * Given a struct clk of a rate-selectable clksel clock, and a register field | |
494 | * value to search for, find the corresponding clock divisor. The register | |
495 | * field value should be pre-masked and shifted down so the LSB is at bit 0 | |
496 | * before calling. Returns 0 on error | |
497 | */ | |
498 | u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val) | |
499 | { | |
500 | const struct clksel *clks; | |
501 | const struct clksel_rate *clkr; | |
502 | ||
503 | clks = omap2_get_clksel_by_parent(clk, clk->parent); | |
fecb494b | 504 | if (!clks) |
543d9378 PW |
505 | return 0; |
506 | ||
507 | for (clkr = clks->rates; clkr->div; clkr++) { | |
508 | if ((clkr->flags & cpu_mask) && (clkr->val == field_val)) | |
509 | break; | |
510 | } | |
511 | ||
512 | if (!clkr->div) { | |
513 | printk(KERN_ERR "clock: Could not find fieldval %d for " | |
514 | "clock %s parent %s\n", field_val, clk->name, | |
515 | clk->parent->name); | |
516 | return 0; | |
517 | } | |
518 | ||
519 | return clkr->div; | |
520 | } | |
521 | ||
522 | /** | |
523 | * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value | |
524 | * @clk: OMAP struct clk to use | |
525 | * @div: integer divisor to search for | |
526 | * | |
527 | * Given a struct clk of a rate-selectable clksel clock, and a clock divisor, | |
528 | * find the corresponding register field value. The return register value is | |
9132f1b4 | 529 | * the value before left-shifting. Returns ~0 on error |
543d9378 PW |
530 | */ |
531 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div) | |
532 | { | |
533 | const struct clksel *clks; | |
534 | const struct clksel_rate *clkr; | |
535 | ||
536 | /* should never happen */ | |
537 | WARN_ON(div == 0); | |
538 | ||
539 | clks = omap2_get_clksel_by_parent(clk, clk->parent); | |
fecb494b | 540 | if (!clks) |
9132f1b4 | 541 | return ~0; |
543d9378 PW |
542 | |
543 | for (clkr = clks->rates; clkr->div; clkr++) { | |
544 | if ((clkr->flags & cpu_mask) && (clkr->div == div)) | |
545 | break; | |
546 | } | |
547 | ||
548 | if (!clkr->div) { | |
549 | printk(KERN_ERR "clock: Could not find divisor %d for " | |
550 | "clock %s parent %s\n", div, clk->name, | |
551 | clk->parent->name); | |
9132f1b4 | 552 | return ~0; |
543d9378 PW |
553 | } |
554 | ||
555 | return clkr->val; | |
556 | } | |
557 | ||
543d9378 PW |
558 | /** |
559 | * omap2_clksel_get_divisor - get current divider applied to parent clock. | |
560 | * @clk: OMAP struct clk to use. | |
561 | * | |
562 | * Returns the integer divisor upon success or 0 on error. | |
563 | */ | |
564 | u32 omap2_clksel_get_divisor(struct clk *clk) | |
565 | { | |
ee1eec36 | 566 | u32 v; |
543d9378 | 567 | |
ee1eec36 | 568 | if (!clk->clksel_mask) |
543d9378 PW |
569 | return 0; |
570 | ||
ee1eec36 PW |
571 | v = __raw_readl(clk->clksel_reg) & clk->clksel_mask; |
572 | v >>= __ffs(clk->clksel_mask); | |
543d9378 | 573 | |
ee1eec36 | 574 | return omap2_clksel_to_divisor(clk, v); |
543d9378 PW |
575 | } |
576 | ||
577 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) | |
578 | { | |
ee1eec36 | 579 | u32 v, field_val, validrate, new_div = 0; |
543d9378 | 580 | |
ee1eec36 | 581 | if (!clk->clksel_mask) |
543d9378 PW |
582 | return -EINVAL; |
583 | ||
ee1eec36 PW |
584 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); |
585 | if (validrate != rate) | |
543d9378 PW |
586 | return -EINVAL; |
587 | ||
588 | field_val = omap2_divisor_to_clksel(clk, new_div); | |
589 | if (field_val == ~0) | |
590 | return -EINVAL; | |
591 | ||
ee1eec36 PW |
592 | v = __raw_readl(clk->clksel_reg); |
593 | v &= ~clk->clksel_mask; | |
594 | v |= field_val << __ffs(clk->clksel_mask); | |
595 | __raw_writel(v, clk->clksel_reg); | |
f11fda6a | 596 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ |
543d9378 PW |
597 | |
598 | clk->rate = clk->parent->rate / new_div; | |
599 | ||
439764cc | 600 | _omap2xxx_clk_commit(clk); |
543d9378 PW |
601 | |
602 | return 0; | |
603 | } | |
604 | ||
605 | ||
606 | /* Set the clock rate for a clock source */ | |
607 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate) | |
608 | { | |
609 | int ret = -EINVAL; | |
610 | ||
611 | pr_debug("clock: set_rate for clock %s to rate %ld\n", clk->name, rate); | |
612 | ||
613 | /* CONFIG_PARTICIPANT clocks are changed only in sets via the | |
614 | rate table mechanism, driven by mpu_speed */ | |
615 | if (clk->flags & CONFIG_PARTICIPANT) | |
616 | return -EINVAL; | |
617 | ||
618 | /* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */ | |
fecb494b | 619 | if (clk->set_rate) |
543d9378 PW |
620 | ret = clk->set_rate(clk, rate); |
621 | ||
543d9378 PW |
622 | return ret; |
623 | } | |
624 | ||
625 | /* | |
626 | * Converts encoded control register address into a full address | |
ee1eec36 | 627 | * On error, the return value (parent_div) will be 0. |
543d9378 | 628 | */ |
ee1eec36 PW |
629 | static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, |
630 | u32 *field_val) | |
543d9378 PW |
631 | { |
632 | const struct clksel *clks; | |
633 | const struct clksel_rate *clkr; | |
634 | ||
543d9378 | 635 | clks = omap2_get_clksel_by_parent(clk, src_clk); |
fecb494b | 636 | if (!clks) |
543d9378 PW |
637 | return 0; |
638 | ||
639 | for (clkr = clks->rates; clkr->div; clkr++) { | |
abf23965 | 640 | if (clkr->flags & cpu_mask && clkr->flags & DEFAULT_RATE) |
543d9378 PW |
641 | break; /* Found the default rate for this platform */ |
642 | } | |
643 | ||
644 | if (!clkr->div) { | |
645 | printk(KERN_ERR "clock: Could not find default rate for " | |
646 | "clock %s parent %s\n", clk->name, | |
647 | src_clk->parent->name); | |
648 | return 0; | |
649 | } | |
650 | ||
651 | /* Should never happen. Add a clksel mask to the struct clk. */ | |
652 | WARN_ON(clk->clksel_mask == 0); | |
653 | ||
ee1eec36 | 654 | *field_val = clkr->val; |
543d9378 | 655 | |
ee1eec36 | 656 | return clkr->div; |
543d9378 PW |
657 | } |
658 | ||
659 | int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) | |
660 | { | |
ee1eec36 | 661 | u32 field_val, v, parent_div; |
543d9378 | 662 | |
fecb494b | 663 | if (clk->flags & CONFIG_PARTICIPANT) |
543d9378 PW |
664 | return -EINVAL; |
665 | ||
666 | if (!clk->clksel) | |
667 | return -EINVAL; | |
668 | ||
ee1eec36 PW |
669 | parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); |
670 | if (!parent_div) | |
543d9378 PW |
671 | return -EINVAL; |
672 | ||
543d9378 | 673 | /* Set new source value (previous dividers if any in effect) */ |
ee1eec36 PW |
674 | v = __raw_readl(clk->clksel_reg); |
675 | v &= ~clk->clksel_mask; | |
676 | v |= field_val << __ffs(clk->clksel_mask); | |
677 | __raw_writel(v, clk->clksel_reg); | |
f11fda6a | 678 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ |
543d9378 | 679 | |
439764cc | 680 | _omap2xxx_clk_commit(clk); |
543d9378 | 681 | |
3f0a820c | 682 | clk_reparent(clk, new_parent); |
41f3103f | 683 | |
543d9378 PW |
684 | /* CLKSEL clocks follow their parents' rates, divided by a divisor */ |
685 | clk->rate = new_parent->rate; | |
686 | ||
687 | if (parent_div > 0) | |
688 | clk->rate /= parent_div; | |
689 | ||
690 | pr_debug("clock: set parent of %s to %s (new rate %ld)\n", | |
691 | clk->name, clk->parent->name, clk->rate); | |
692 | ||
543d9378 PW |
693 | return 0; |
694 | } | |
695 | ||
696 | /*------------------------------------------------------------------------- | |
697 | * Omap2 clock reset and init functions | |
698 | *-------------------------------------------------------------------------*/ | |
699 | ||
700 | #ifdef CONFIG_OMAP_RESET_CLOCKS | |
701 | void omap2_clk_disable_unused(struct clk *clk) | |
702 | { | |
703 | u32 regval32, v; | |
704 | ||
705 | v = (clk->flags & INVERT_ENABLE) ? (1 << clk->enable_bit) : 0; | |
706 | ||
707 | regval32 = __raw_readl(clk->enable_reg); | |
708 | if ((regval32 & (1 << clk->enable_bit)) == v) | |
709 | return; | |
710 | ||
0db4e825 | 711 | printk(KERN_DEBUG "Disabling unused clock \"%s\"\n", clk->name); |
8463e20a TK |
712 | if (cpu_is_omap34xx()) { |
713 | omap2_clk_enable(clk); | |
714 | omap2_clk_disable(clk); | |
715 | } else | |
716 | _omap2_clk_disable(clk); | |
fe617af7 PDS |
717 | if (clk->clkdm != NULL) |
718 | pwrdm_clkdm_state_switch(clk->clkdm); | |
543d9378 PW |
719 | } |
720 | #endif |