diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clkt_clksel.c | 417 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.c | 377 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.h | 2 |
4 files changed, 424 insertions, 375 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 1a135c8e7f1e..9ecc58d2c986 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile | |||
@@ -9,7 +9,8 @@ omap-2-3-common = irq.o sdrc.o omap_hwmod.o | |||
9 | omap-3-4-common = dpll3xxx.o | 9 | omap-3-4-common = dpll3xxx.o |
10 | prcm-common = prcm.o powerdomain.o | 10 | prcm-common = prcm.o powerdomain.o |
11 | clock-common = clock.o clock_common_data.o \ | 11 | clock-common = clock.o clock_common_data.o \ |
12 | clockdomain.o clkt_dpll.o | 12 | clockdomain.o clkt_dpll.o \ |
13 | clkt_clksel.o | ||
13 | 14 | ||
14 | obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(prcm-common) $(clock-common) | 15 | obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(prcm-common) $(clock-common) |
15 | obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(prcm-common) $(clock-common) \ | 16 | obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(prcm-common) $(clock-common) \ |
diff --git a/arch/arm/mach-omap2/clkt_clksel.c b/arch/arm/mach-omap2/clkt_clksel.c new file mode 100644 index 000000000000..25a2363106de --- /dev/null +++ b/arch/arm/mach-omap2/clkt_clksel.c | |||
@@ -0,0 +1,417 @@ | |||
1 | /* | ||
2 | * clkt_clksel.c - OMAP2/3/4 clksel clock functions | ||
3 | * | ||
4 | * Copyright (C) 2005-2008 Texas Instruments, Inc. | ||
5 | * Copyright (C) 2004-2010 Nokia Corporation | ||
6 | * | ||
7 | * Contacts: | ||
8 | * Richard Woodruff <r-woodruff2@ti.com> | ||
9 | * Paul Walmsley | ||
10 | * | ||
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 | * XXX At some point these clksel clocks should be split into | ||
16 | * "divider" clocks and "mux" clocks to better match the hardware. | ||
17 | * | ||
18 | * XXX Currently these clocks are only used in the OMAP2/3/4 code, but | ||
19 | * many of the OMAP1 clocks should be convertible to use this | ||
20 | * mechanism. | ||
21 | */ | ||
22 | #undef DEBUG | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/io.h> | ||
28 | |||
29 | #include <plat/clock.h> | ||
30 | |||
31 | #include "clock.h" | ||
32 | #include "cm.h" | ||
33 | #include "cm-regbits-24xx.h" | ||
34 | #include "cm-regbits-34xx.h" | ||
35 | |||
36 | /* Private functions */ | ||
37 | |||
38 | /** | ||
39 | * _omap2_get_clksel_by_parent - return clksel struct for a given clk & parent | ||
40 | * @clk: OMAP struct clk ptr to inspect | ||
41 | * @src_clk: OMAP struct clk ptr of the parent clk to search for | ||
42 | * | ||
43 | * Scan the struct clksel array associated with the clock to find | ||
44 | * the element associated with the supplied parent clock address. | ||
45 | * Returns a pointer to the struct clksel on success or NULL on error. | ||
46 | */ | ||
47 | static const struct clksel *_omap2_get_clksel_by_parent(struct clk *clk, | ||
48 | struct clk *src_clk) | ||
49 | { | ||
50 | const struct clksel *clks; | ||
51 | |||
52 | if (!clk->clksel) | ||
53 | return NULL; | ||
54 | |||
55 | for (clks = clk->clksel; clks->parent; clks++) { | ||
56 | if (clks->parent == src_clk) | ||
57 | break; /* Found the requested parent */ | ||
58 | } | ||
59 | |||
60 | if (!clks->parent) { | ||
61 | printk(KERN_ERR "clock: Could not find parent clock %s in " | ||
62 | "clksel array of clock %s\n", src_clk->name, | ||
63 | clk->name); | ||
64 | return NULL; | ||
65 | } | ||
66 | |||
67 | return clks; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Converts encoded control register address into a full address | ||
72 | * On error, the return value (parent_div) will be 0. | ||
73 | */ | ||
74 | static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, | ||
75 | u32 *field_val) | ||
76 | { | ||
77 | const struct clksel *clks; | ||
78 | const struct clksel_rate *clkr; | ||
79 | |||
80 | clks = _omap2_get_clksel_by_parent(clk, src_clk); | ||
81 | if (!clks) | ||
82 | return 0; | ||
83 | |||
84 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
85 | if (clkr->flags & cpu_mask && clkr->flags & DEFAULT_RATE) | ||
86 | break; /* Found the default rate for this platform */ | ||
87 | } | ||
88 | |||
89 | if (!clkr->div) { | ||
90 | printk(KERN_ERR "clock: Could not find default rate for " | ||
91 | "clock %s parent %s\n", clk->name, | ||
92 | src_clk->parent->name); | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | /* Should never happen. Add a clksel mask to the struct clk. */ | ||
97 | WARN_ON(clk->clksel_mask == 0); | ||
98 | |||
99 | *field_val = clkr->val; | ||
100 | |||
101 | return clkr->div; | ||
102 | } | ||
103 | |||
104 | |||
105 | /* Public functions */ | ||
106 | |||
107 | /** | ||
108 | * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware | ||
109 | * @clk: OMAP clock struct ptr to use | ||
110 | * | ||
111 | * Given a pointer to a source-selectable struct clk, read the hardware | ||
112 | * register and determine what its parent is currently set to. Update the | ||
113 | * clk->parent field with the appropriate clk ptr. | ||
114 | */ | ||
115 | void omap2_init_clksel_parent(struct clk *clk) | ||
116 | { | ||
117 | const struct clksel *clks; | ||
118 | const struct clksel_rate *clkr; | ||
119 | u32 r, found = 0; | ||
120 | |||
121 | if (!clk->clksel) | ||
122 | return; | ||
123 | |||
124 | r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | ||
125 | r >>= __ffs(clk->clksel_mask); | ||
126 | |||
127 | for (clks = clk->clksel; clks->parent && !found; clks++) { | ||
128 | for (clkr = clks->rates; clkr->div && !found; clkr++) { | ||
129 | if ((clkr->flags & cpu_mask) && (clkr->val == r)) { | ||
130 | if (clk->parent != clks->parent) { | ||
131 | pr_debug("clock: inited %s parent " | ||
132 | "to %s (was %s)\n", | ||
133 | clk->name, clks->parent->name, | ||
134 | ((clk->parent) ? | ||
135 | clk->parent->name : "NULL")); | ||
136 | clk_reparent(clk, clks->parent); | ||
137 | }; | ||
138 | found = 1; | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | if (!found) | ||
144 | printk(KERN_ERR "clock: init parent: could not find " | ||
145 | "regval %0x for clock %s\n", r, clk->name); | ||
146 | |||
147 | return; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Used for clocks that are part of CLKSEL_xyz governed clocks. | ||
152 | * REVISIT: Maybe change to use clk->enable() functions like on omap1? | ||
153 | */ | ||
154 | unsigned long omap2_clksel_recalc(struct clk *clk) | ||
155 | { | ||
156 | unsigned long rate; | ||
157 | u32 div = 0; | ||
158 | |||
159 | pr_debug("clock: recalc'ing clksel clk %s\n", clk->name); | ||
160 | |||
161 | div = omap2_clksel_get_divisor(clk); | ||
162 | if (div == 0) | ||
163 | return clk->rate; | ||
164 | |||
165 | rate = clk->parent->rate / div; | ||
166 | |||
167 | pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div); | ||
168 | |||
169 | return rate; | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * omap2_clksel_round_rate_div - find divisor for the given clock and rate | ||
174 | * @clk: OMAP struct clk to use | ||
175 | * @target_rate: desired clock rate | ||
176 | * @new_div: ptr to where we should store the divisor | ||
177 | * | ||
178 | * Finds 'best' divider value in an array based on the source and target | ||
179 | * rates. The divider array must be sorted with smallest divider first. | ||
180 | * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, | ||
181 | * they are only settable as part of virtual_prcm set. | ||
182 | * | ||
183 | * Returns the rounded clock rate or returns 0xffffffff on error. | ||
184 | */ | ||
185 | u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | ||
186 | u32 *new_div) | ||
187 | { | ||
188 | unsigned long test_rate; | ||
189 | const struct clksel *clks; | ||
190 | const struct clksel_rate *clkr; | ||
191 | u32 last_div = 0; | ||
192 | |||
193 | pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n", | ||
194 | clk->name, target_rate); | ||
195 | |||
196 | *new_div = 1; | ||
197 | |||
198 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | ||
199 | if (!clks) | ||
200 | return ~0; | ||
201 | |||
202 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
203 | if (!(clkr->flags & cpu_mask)) | ||
204 | continue; | ||
205 | |||
206 | /* Sanity check */ | ||
207 | if (clkr->div <= last_div) | ||
208 | pr_err("clock: clksel_rate table not sorted " | ||
209 | "for clock %s", clk->name); | ||
210 | |||
211 | last_div = clkr->div; | ||
212 | |||
213 | test_rate = clk->parent->rate / clkr->div; | ||
214 | |||
215 | if (test_rate <= target_rate) | ||
216 | break; /* found it */ | ||
217 | } | ||
218 | |||
219 | if (!clkr->div) { | ||
220 | pr_err("clock: Could not find divisor for target " | ||
221 | "rate %ld for clock %s parent %s\n", target_rate, | ||
222 | clk->name, clk->parent->name); | ||
223 | return ~0; | ||
224 | } | ||
225 | |||
226 | *new_div = clkr->div; | ||
227 | |||
228 | pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div, | ||
229 | (clk->parent->rate / clkr->div)); | ||
230 | |||
231 | return clk->parent->rate / clkr->div; | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * omap2_clksel_round_rate - find rounded rate for the given clock and rate | ||
236 | * @clk: OMAP struct clk to use | ||
237 | * @target_rate: desired clock rate | ||
238 | * | ||
239 | * Compatibility wrapper for OMAP clock framework | ||
240 | * Finds best target rate based on the source clock and possible dividers. | ||
241 | * rates. The divider array must be sorted with smallest divider first. | ||
242 | * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, | ||
243 | * they are only settable as part of virtual_prcm set. | ||
244 | * | ||
245 | * Returns the rounded clock rate or returns 0xffffffff on error. | ||
246 | */ | ||
247 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate) | ||
248 | { | ||
249 | u32 new_div; | ||
250 | |||
251 | return omap2_clksel_round_rate_div(clk, target_rate, &new_div); | ||
252 | } | ||
253 | |||
254 | |||
255 | /* Given a clock and a rate apply a clock specific rounding function */ | ||
256 | long omap2_clk_round_rate(struct clk *clk, unsigned long rate) | ||
257 | { | ||
258 | if (clk->round_rate) | ||
259 | return clk->round_rate(clk, rate); | ||
260 | |||
261 | if (clk->flags & RATE_FIXED) | ||
262 | printk(KERN_ERR "clock: generic omap2_clk_round_rate called " | ||
263 | "on fixed-rate clock %s\n", clk->name); | ||
264 | |||
265 | return clk->rate; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * omap2_clksel_to_divisor() - turn clksel field value into integer divider | ||
270 | * @clk: OMAP struct clk to use | ||
271 | * @field_val: register field value to find | ||
272 | * | ||
273 | * Given a struct clk of a rate-selectable clksel clock, and a register field | ||
274 | * value to search for, find the corresponding clock divisor. The register | ||
275 | * field value should be pre-masked and shifted down so the LSB is at bit 0 | ||
276 | * before calling. Returns 0 on error | ||
277 | */ | ||
278 | u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val) | ||
279 | { | ||
280 | const struct clksel *clks; | ||
281 | const struct clksel_rate *clkr; | ||
282 | |||
283 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | ||
284 | if (!clks) | ||
285 | return 0; | ||
286 | |||
287 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
288 | if ((clkr->flags & cpu_mask) && (clkr->val == field_val)) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | if (!clkr->div) { | ||
293 | printk(KERN_ERR "clock: Could not find fieldval %d for " | ||
294 | "clock %s parent %s\n", field_val, clk->name, | ||
295 | clk->parent->name); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | return clkr->div; | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value | ||
304 | * @clk: OMAP struct clk to use | ||
305 | * @div: integer divisor to search for | ||
306 | * | ||
307 | * Given a struct clk of a rate-selectable clksel clock, and a clock divisor, | ||
308 | * find the corresponding register field value. The return register value is | ||
309 | * the value before left-shifting. Returns ~0 on error | ||
310 | */ | ||
311 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div) | ||
312 | { | ||
313 | const struct clksel *clks; | ||
314 | const struct clksel_rate *clkr; | ||
315 | |||
316 | /* should never happen */ | ||
317 | WARN_ON(div == 0); | ||
318 | |||
319 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | ||
320 | if (!clks) | ||
321 | return ~0; | ||
322 | |||
323 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
324 | if ((clkr->flags & cpu_mask) && (clkr->div == div)) | ||
325 | break; | ||
326 | } | ||
327 | |||
328 | if (!clkr->div) { | ||
329 | printk(KERN_ERR "clock: Could not find divisor %d for " | ||
330 | "clock %s parent %s\n", div, clk->name, | ||
331 | clk->parent->name); | ||
332 | return ~0; | ||
333 | } | ||
334 | |||
335 | return clkr->val; | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * omap2_clksel_get_divisor - get current divider applied to parent clock. | ||
340 | * @clk: OMAP struct clk to use. | ||
341 | * | ||
342 | * Returns the integer divisor upon success or 0 on error. | ||
343 | */ | ||
344 | u32 omap2_clksel_get_divisor(struct clk *clk) | ||
345 | { | ||
346 | u32 v; | ||
347 | |||
348 | if (!clk->clksel_mask) | ||
349 | return 0; | ||
350 | |||
351 | v = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | ||
352 | v >>= __ffs(clk->clksel_mask); | ||
353 | |||
354 | return omap2_clksel_to_divisor(clk, v); | ||
355 | } | ||
356 | |||
357 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) | ||
358 | { | ||
359 | u32 v, field_val, validrate, new_div = 0; | ||
360 | |||
361 | if (!clk->clksel_mask) | ||
362 | return -EINVAL; | ||
363 | |||
364 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); | ||
365 | if (validrate != rate) | ||
366 | return -EINVAL; | ||
367 | |||
368 | field_val = omap2_divisor_to_clksel(clk, new_div); | ||
369 | if (field_val == ~0) | ||
370 | return -EINVAL; | ||
371 | |||
372 | v = __raw_readl(clk->clksel_reg); | ||
373 | v &= ~clk->clksel_mask; | ||
374 | v |= field_val << __ffs(clk->clksel_mask); | ||
375 | __raw_writel(v, clk->clksel_reg); | ||
376 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ | ||
377 | |||
378 | clk->rate = clk->parent->rate / new_div; | ||
379 | |||
380 | omap2xxx_clk_commit(clk); | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) | ||
386 | { | ||
387 | u32 field_val, v, parent_div; | ||
388 | |||
389 | if (!clk->clksel) | ||
390 | return -EINVAL; | ||
391 | |||
392 | parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); | ||
393 | if (!parent_div) | ||
394 | return -EINVAL; | ||
395 | |||
396 | /* Set new source value (previous dividers if any in effect) */ | ||
397 | v = __raw_readl(clk->clksel_reg); | ||
398 | v &= ~clk->clksel_mask; | ||
399 | v |= field_val << __ffs(clk->clksel_mask); | ||
400 | __raw_writel(v, clk->clksel_reg); | ||
401 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ | ||
402 | |||
403 | omap2xxx_clk_commit(clk); | ||
404 | |||
405 | clk_reparent(clk, new_parent); | ||
406 | |||
407 | /* CLKSEL clocks follow their parents' rates, divided by a divisor */ | ||
408 | clk->rate = new_parent->rate; | ||
409 | |||
410 | if (parent_div > 0) | ||
411 | clk->rate /= parent_div; | ||
412 | |||
413 | pr_debug("clock: set parent of %s to %s (new rate %ld)\n", | ||
414 | clk->name, clk->parent->name, clk->rate); | ||
415 | |||
416 | return 0; | ||
417 | } | ||
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index 98196285e80c..d3ebb74873f8 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c | |||
@@ -43,14 +43,14 @@ u8 cpu_mask; | |||
43 | *-------------------------------------------------------------------------*/ | 43 | *-------------------------------------------------------------------------*/ |
44 | 44 | ||
45 | /** | 45 | /** |
46 | * _omap2xxx_clk_commit - commit clock parent/rate changes in hardware | 46 | * omap2xxx_clk_commit - commit clock parent/rate changes in hardware |
47 | * @clk: struct clk * | 47 | * @clk: struct clk * |
48 | * | 48 | * |
49 | * If @clk has the DELAYED_APP flag set, meaning that parent/rate changes | 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 | 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. | 51 | * VALID_CONFIG bit and wait for the write to complete. No return value. |
52 | */ | 52 | */ |
53 | static void _omap2xxx_clk_commit(struct clk *clk) | 53 | void omap2xxx_clk_commit(struct clk *clk) |
54 | { | 54 | { |
55 | if (!cpu_is_omap24xx()) | 55 | if (!cpu_is_omap24xx()) |
56 | return; | 56 | return; |
@@ -91,49 +91,6 @@ void omap2_init_clk_clkdm(struct clk *clk) | |||
91 | } | 91 | } |
92 | 92 | ||
93 | /** | 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")); | ||
122 | clk_reparent(clk, clks->parent); | ||
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 | |||
136 | /** | ||
137 | * omap2_clk_dflt_find_companion - find companion clock to @clk | 94 | * omap2_clk_dflt_find_companion - find companion clock to @clk |
138 | * @clk: struct clk * to find the companion clock of | 95 | * @clk: struct clk * to find the companion clock of |
139 | * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in | 96 | * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in |
@@ -335,274 +292,6 @@ err: | |||
335 | return ret; | 292 | return ret; |
336 | } | 293 | } |
337 | 294 | ||
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 | */ | ||
342 | unsigned long omap2_clksel_recalc(struct clk *clk) | ||
343 | { | ||
344 | unsigned long rate; | ||
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) | ||
351 | return clk->rate; | ||
352 | |||
353 | rate = clk->parent->rate / div; | ||
354 | |||
355 | pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div); | ||
356 | |||
357 | return rate; | ||
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 | */ | ||
369 | static const struct clksel *omap2_get_clksel_by_parent(struct clk *clk, | ||
370 | struct clk *src_clk) | ||
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 | |||
413 | pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n", | ||
414 | clk->name, target_rate); | ||
415 | |||
416 | *new_div = 1; | ||
417 | |||
418 | clks = omap2_get_clksel_by_parent(clk, clk->parent); | ||
419 | if (!clks) | ||
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) | ||
428 | pr_err("clock: clksel_rate table not sorted " | ||
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) { | ||
440 | pr_err("clock: Could not find divisor for target " | ||
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 | |||
448 | pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div, | ||
449 | (clk->parent->rate / clkr->div)); | ||
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 | { | ||
478 | if (clk->round_rate) | ||
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); | ||
504 | if (!clks) | ||
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 | ||
529 | * the value before left-shifting. Returns ~0 on error | ||
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); | ||
540 | if (!clks) | ||
541 | return ~0; | ||
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); | ||
552 | return ~0; | ||
553 | } | ||
554 | |||
555 | return clkr->val; | ||
556 | } | ||
557 | |||
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 | { | ||
566 | u32 v; | ||
567 | |||
568 | if (!clk->clksel_mask) | ||
569 | return 0; | ||
570 | |||
571 | v = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | ||
572 | v >>= __ffs(clk->clksel_mask); | ||
573 | |||
574 | return omap2_clksel_to_divisor(clk, v); | ||
575 | } | ||
576 | |||
577 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) | ||
578 | { | ||
579 | u32 v, field_val, validrate, new_div = 0; | ||
580 | |||
581 | if (!clk->clksel_mask) | ||
582 | return -EINVAL; | ||
583 | |||
584 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); | ||
585 | if (validrate != rate) | ||
586 | return -EINVAL; | ||
587 | |||
588 | field_val = omap2_divisor_to_clksel(clk, new_div); | ||
589 | if (field_val == ~0) | ||
590 | return -EINVAL; | ||
591 | |||
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); | ||
596 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ | ||
597 | |||
598 | clk->rate = clk->parent->rate / new_div; | ||
599 | |||
600 | _omap2xxx_clk_commit(clk); | ||
601 | |||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | |||
606 | /* Set the clock rate for a clock source */ | 295 | /* Set the clock rate for a clock source */ |
607 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate) | 296 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate) |
608 | { | 297 | { |
@@ -622,75 +311,15 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate) | |||
622 | return ret; | 311 | return ret; |
623 | } | 312 | } |
624 | 313 | ||
625 | /* | ||
626 | * Converts encoded control register address into a full address | ||
627 | * On error, the return value (parent_div) will be 0. | ||
628 | */ | ||
629 | static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, | ||
630 | u32 *field_val) | ||
631 | { | ||
632 | const struct clksel *clks; | ||
633 | const struct clksel_rate *clkr; | ||
634 | |||
635 | clks = omap2_get_clksel_by_parent(clk, src_clk); | ||
636 | if (!clks) | ||
637 | return 0; | ||
638 | |||
639 | for (clkr = clks->rates; clkr->div; clkr++) { | ||
640 | if (clkr->flags & cpu_mask && clkr->flags & DEFAULT_RATE) | ||
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 | |||
654 | *field_val = clkr->val; | ||
655 | |||
656 | return clkr->div; | ||
657 | } | ||
658 | |||
659 | int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) | 314 | int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) |
660 | { | 315 | { |
661 | u32 field_val, v, parent_div; | ||
662 | |||
663 | if (clk->flags & CONFIG_PARTICIPANT) | 316 | if (clk->flags & CONFIG_PARTICIPANT) |
664 | return -EINVAL; | 317 | return -EINVAL; |
665 | 318 | ||
666 | if (!clk->clksel) | 319 | if (!clk->clksel) |
667 | return -EINVAL; | 320 | return -EINVAL; |
668 | 321 | ||
669 | parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); | 322 | return omap2_clksel_set_parent(clk, new_parent); |
670 | if (!parent_div) | ||
671 | return -EINVAL; | ||
672 | |||
673 | /* Set new source value (previous dividers if any in effect) */ | ||
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); | ||
678 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ | ||
679 | |||
680 | _omap2xxx_clk_commit(clk); | ||
681 | |||
682 | clk_reparent(clk, new_parent); | ||
683 | |||
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 | |||
693 | return 0; | ||
694 | } | 323 | } |
695 | 324 | ||
696 | /*------------------------------------------------------------------------- | 325 | /*------------------------------------------------------------------------- |
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index 0d70dc09370b..0d21702a3cdd 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h | |||
@@ -80,6 +80,7 @@ u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val); | |||
80 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div); | 80 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div); |
81 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate); | 81 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate); |
82 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate); | 82 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate); |
83 | int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent); | ||
83 | u32 omap2_get_dpll_rate(struct clk *clk); | 84 | u32 omap2_get_dpll_rate(struct clk *clk); |
84 | void omap2_init_dpll_parent(struct clk *clk); | 85 | void omap2_init_dpll_parent(struct clk *clk); |
85 | int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name); | 86 | int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name); |
@@ -90,6 +91,7 @@ void omap2_clk_dflt_find_companion(struct clk *clk, void __iomem **other_reg, | |||
90 | u8 *other_bit); | 91 | u8 *other_bit); |
91 | void omap2_clk_dflt_find_idlest(struct clk *clk, void __iomem **idlest_reg, | 92 | void omap2_clk_dflt_find_idlest(struct clk *clk, void __iomem **idlest_reg, |
92 | u8 *idlest_bit); | 93 | u8 *idlest_bit); |
94 | void omap2xxx_clk_commit(struct clk *clk); | ||
93 | 95 | ||
94 | extern u8 cpu_mask; | 96 | extern u8 cpu_mask; |
95 | 97 | ||