aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2010-01-26 22:13:04 -0500
committerPaul Walmsley <paul@pwsan.com>2010-01-26 22:13:04 -0500
commitdf791b3ebf181b3eece9c770565fcf0844bbd7cb (patch)
tree63f9c589296060f84da3d8d0dd63031630aa0069 /arch/arm
parent0b96af683026ab9ca4dd52f9005a1a4fc582e914 (diff)
OMAP2/3/4 clock: move clksel clock functions into mach-omap2/clkt_clksel.c
Move all clksel-related clock functions from mach-omap2/clock.c to mach-omap2/clkt_clksel.c. This is intended to make the clock code easier to understand, since all of the functions needed to manage clksel clocks are now located in their own file, rather than being mixed with other, unrelated functions. Clock debugging is also now more finely-grained, since the DEBUG macro can now be defined for clksel clocks alon. This should reduce unnecessary console noise when debugging. Also, if at some future point the mach-omap2/ directory is split into OMAP2/3/4 variants, this clkt file can be moved to the plat-omap/ directory to be shared. Thanks to Alexander Shishkin <virtuoso@slind.org> for his comments to improve the patch description. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Alexander Shishkin <virtuoso@slind.org>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-omap2/Makefile3
-rw-r--r--arch/arm/mach-omap2/clkt_clksel.c417
-rw-r--r--arch/arm/mach-omap2/clock.c377
-rw-r--r--arch/arm/mach-omap2/clock.h2
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
9omap-3-4-common = dpll3xxx.o 9omap-3-4-common = dpll3xxx.o
10prcm-common = prcm.o powerdomain.o 10prcm-common = prcm.o powerdomain.o
11clock-common = clock.o clock_common_data.o \ 11clock-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
14obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(prcm-common) $(clock-common) 15obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(prcm-common) $(clock-common)
15obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(prcm-common) $(clock-common) \ 16obj-$(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 */
47static 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 */
74static 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 */
115void 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 */
154unsigned 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 */
185u32 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 */
247long 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 */
256long 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 */
278u32 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 */
311u32 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 */
344u32 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
357int 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
385int 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 */
53static void _omap2xxx_clk_commit(struct clk *clk) 53void 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 */
101void 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 */
342unsigned 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 */
369static 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 */
405u32 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 */
467long 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 */
476long 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 */
498u32 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 */
531u32 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 */
564u32 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
577int 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 */
607int omap2_clk_set_rate(struct clk *clk, unsigned long rate) 296int 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 */
629static 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
659int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) 314int 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);
80u32 omap2_divisor_to_clksel(struct clk *clk, u32 div); 80u32 omap2_divisor_to_clksel(struct clk *clk, u32 div);
81long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate); 81long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate);
82int omap2_clksel_set_rate(struct clk *clk, unsigned long rate); 82int omap2_clksel_set_rate(struct clk *clk, unsigned long rate);
83int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent);
83u32 omap2_get_dpll_rate(struct clk *clk); 84u32 omap2_get_dpll_rate(struct clk *clk);
84void omap2_init_dpll_parent(struct clk *clk); 85void omap2_init_dpll_parent(struct clk *clk);
85int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name); 86int 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);
91void omap2_clk_dflt_find_idlest(struct clk *clk, void __iomem **idlest_reg, 92void omap2_clk_dflt_find_idlest(struct clk *clk, void __iomem **idlest_reg,
92 u8 *idlest_bit); 93 u8 *idlest_bit);
94void omap2xxx_clk_commit(struct clk *clk);
93 95
94extern u8 cpu_mask; 96extern u8 cpu_mask;
95 97