OMAP2+ clock: remove DEFAULT_RATE clksel_rate flag
[pandora-kernel.git] / arch / arm / mach-omap2 / clkt_clksel.c
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  * _omap2_clksel_get_src_field - find the new clksel divisor to use
72  * @src_clk: planned new parent struct clk *
73  * @clk: struct clk * that is being reparented
74  * @field_val: pointer to a u32 to contain the register data for the divisor
75  *
76  * Given an intended new parent struct clk * @src_clk, and the struct
77  * clk * @clk to the clock that is being reparented, find the
78  * appropriate rate divisor for the new clock (returned as the return
79  * value), and the corresponding register bitfield data to program to
80  * reach that divisor (returned in the u32 pointed to by @field_val).
81  * Returns 0 on error, or returns the newly-selected divisor upon
82  * success (in this latter case, the corresponding register bitfield
83  * value is passed back in the variable pointed to by @field_val)
84  */
85 static u8 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk,
86                                       u32 *field_val)
87 {
88         const struct clksel *clks;
89         const struct clksel_rate *clkr, *max_clkr;
90         u8 max_div = 0;
91
92         clks = _omap2_get_clksel_by_parent(clk, src_clk);
93         if (!clks)
94                 return 0;
95
96         /*
97          * Find the highest divisor (e.g., the one resulting in the
98          * lowest rate) to use as the default.  This should avoid
99          * clock rates that are too high for the device.  XXX A better
100          * solution here would be to try to determine if there is a
101          * divisor matching the original clock rate before the parent
102          * switch, and if it cannot be found, to fall back to the
103          * highest divisor.
104          */
105         for (clkr = clks->rates; clkr->div; clkr++) {
106                 if (!(clkr->flags & cpu_mask))
107                         continue;
108
109                 if (clkr->div > max_div) {
110                         max_div = clkr->div;
111                         max_clkr = clkr;
112                 }
113         }
114
115         if (max_div == 0) {
116                 WARN(1, "clock: Could not find divisor for "
117                        "clock %s parent %s\n", clk->name,
118                        src_clk->parent->name);
119                 return 0;
120         }
121
122         *field_val = max_clkr->val;
123
124         return max_div;
125 }
126
127
128 /* Public functions */
129
130 /**
131  * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware
132  * @clk: OMAP clock struct ptr to use
133  *
134  * Given a pointer to a source-selectable struct clk, read the hardware
135  * register and determine what its parent is currently set to.  Update the
136  * clk->parent field with the appropriate clk ptr.
137  */
138 void omap2_init_clksel_parent(struct clk *clk)
139 {
140         const struct clksel *clks;
141         const struct clksel_rate *clkr;
142         u32 r, found = 0;
143
144         if (!clk->clksel)
145                 return;
146
147         r = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
148         r >>= __ffs(clk->clksel_mask);
149
150         for (clks = clk->clksel; clks->parent && !found; clks++) {
151                 for (clkr = clks->rates; clkr->div && !found; clkr++) {
152                         if ((clkr->flags & cpu_mask) && (clkr->val == r)) {
153                                 if (clk->parent != clks->parent) {
154                                         pr_debug("clock: inited %s parent "
155                                                  "to %s (was %s)\n",
156                                                  clk->name, clks->parent->name,
157                                                  ((clk->parent) ?
158                                                   clk->parent->name : "NULL"));
159                                         clk_reparent(clk, clks->parent);
160                                 };
161                                 found = 1;
162                         }
163                 }
164         }
165
166         if (!found)
167                 printk(KERN_ERR "clock: init parent: could not find "
168                        "regval %0x for clock %s\n", r,  clk->name);
169
170         return;
171 }
172
173 /*
174  * Used for clocks that are part of CLKSEL_xyz governed clocks.
175  * REVISIT: Maybe change to use clk->enable() functions like on omap1?
176  */
177 unsigned long omap2_clksel_recalc(struct clk *clk)
178 {
179         unsigned long rate;
180         u32 div = 0;
181
182         pr_debug("clock: recalc'ing clksel clk %s\n", clk->name);
183
184         div = omap2_clksel_get_divisor(clk);
185         if (div == 0)
186                 return clk->rate;
187
188         rate = clk->parent->rate / div;
189
190         pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div);
191
192         return rate;
193 }
194
195 /**
196  * omap2_clksel_round_rate_div - find divisor for the given clock and rate
197  * @clk: OMAP struct clk to use
198  * @target_rate: desired clock rate
199  * @new_div: ptr to where we should store the divisor
200  *
201  * Finds 'best' divider value in an array based on the source and target
202  * rates.  The divider array must be sorted with smallest divider first.
203  *
204  * Returns the rounded clock rate or returns 0xffffffff on error.
205  */
206 u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
207                                 u32 *new_div)
208 {
209         unsigned long test_rate;
210         const struct clksel *clks;
211         const struct clksel_rate *clkr;
212         u32 last_div = 0;
213
214         pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n",
215                  clk->name, target_rate);
216
217         *new_div = 1;
218
219         clks = _omap2_get_clksel_by_parent(clk, clk->parent);
220         if (!clks)
221                 return ~0;
222
223         for (clkr = clks->rates; clkr->div; clkr++) {
224                 if (!(clkr->flags & cpu_mask))
225                         continue;
226
227                 /* Sanity check */
228                 if (clkr->div <= last_div)
229                         pr_err("clock: clksel_rate table not sorted "
230                                "for clock %s", clk->name);
231
232                 last_div = clkr->div;
233
234                 test_rate = clk->parent->rate / clkr->div;
235
236                 if (test_rate <= target_rate)
237                         break; /* found it */
238         }
239
240         if (!clkr->div) {
241                 pr_err("clock: Could not find divisor for target "
242                        "rate %ld for clock %s parent %s\n", target_rate,
243                        clk->name, clk->parent->name);
244                 return ~0;
245         }
246
247         *new_div = clkr->div;
248
249         pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div,
250                  (clk->parent->rate / clkr->div));
251
252         return clk->parent->rate / clkr->div;
253 }
254
255 /**
256  * omap2_clksel_round_rate - find rounded rate for the given clock and rate
257  * @clk: OMAP struct clk to use
258  * @target_rate: desired clock rate
259  *
260  * Compatibility wrapper for OMAP clock framework
261  * Finds best target rate based on the source clock and possible dividers.
262  * rates. The divider array must be sorted with smallest divider first.
263  * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT,
264  * they are only settable as part of virtual_prcm set.
265  *
266  * Returns the rounded clock rate or returns 0xffffffff on error.
267  */
268 long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate)
269 {
270         u32 new_div;
271
272         return omap2_clksel_round_rate_div(clk, target_rate, &new_div);
273 }
274
275
276 /* Given a clock and a rate apply a clock specific rounding function */
277 long omap2_clk_round_rate(struct clk *clk, unsigned long rate)
278 {
279         if (clk->round_rate)
280                 return clk->round_rate(clk, rate);
281
282         return clk->rate;
283 }
284
285 /**
286  * omap2_clksel_to_divisor() - turn clksel field value into integer divider
287  * @clk: OMAP struct clk to use
288  * @field_val: register field value to find
289  *
290  * Given a struct clk of a rate-selectable clksel clock, and a register field
291  * value to search for, find the corresponding clock divisor.  The register
292  * field value should be pre-masked and shifted down so the LSB is at bit 0
293  * before calling.  Returns 0 on error
294  */
295 u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val)
296 {
297         const struct clksel *clks;
298         const struct clksel_rate *clkr;
299
300         clks = _omap2_get_clksel_by_parent(clk, clk->parent);
301         if (!clks)
302                 return 0;
303
304         for (clkr = clks->rates; clkr->div; clkr++) {
305                 if ((clkr->flags & cpu_mask) && (clkr->val == field_val))
306                         break;
307         }
308
309         if (!clkr->div) {
310                 printk(KERN_ERR "clock: Could not find fieldval %d for "
311                        "clock %s parent %s\n", field_val, clk->name,
312                        clk->parent->name);
313                 return 0;
314         }
315
316         return clkr->div;
317 }
318
319 /**
320  * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value
321  * @clk: OMAP struct clk to use
322  * @div: integer divisor to search for
323  *
324  * Given a struct clk of a rate-selectable clksel clock, and a clock divisor,
325  * find the corresponding register field value.  The return register value is
326  * the value before left-shifting.  Returns ~0 on error
327  */
328 u32 omap2_divisor_to_clksel(struct clk *clk, u32 div)
329 {
330         const struct clksel *clks;
331         const struct clksel_rate *clkr;
332
333         /* should never happen */
334         WARN_ON(div == 0);
335
336         clks = _omap2_get_clksel_by_parent(clk, clk->parent);
337         if (!clks)
338                 return ~0;
339
340         for (clkr = clks->rates; clkr->div; clkr++) {
341                 if ((clkr->flags & cpu_mask) && (clkr->div == div))
342                         break;
343         }
344
345         if (!clkr->div) {
346                 printk(KERN_ERR "clock: Could not find divisor %d for "
347                        "clock %s parent %s\n", div, clk->name,
348                        clk->parent->name);
349                 return ~0;
350         }
351
352         return clkr->val;
353 }
354
355 /**
356  * omap2_clksel_get_divisor - get current divider applied to parent clock.
357  * @clk: OMAP struct clk to use.
358  *
359  * Returns the integer divisor upon success or 0 on error.
360  */
361 u32 omap2_clksel_get_divisor(struct clk *clk)
362 {
363         u32 v;
364
365         if (!clk->clksel_mask)
366                 return 0;
367
368         v = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
369         v >>= __ffs(clk->clksel_mask);
370
371         return omap2_clksel_to_divisor(clk, v);
372 }
373
374 int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
375 {
376         u32 v, field_val, validrate, new_div = 0;
377
378         if (!clk->clksel_mask)
379                 return -EINVAL;
380
381         validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
382         if (validrate != rate)
383                 return -EINVAL;
384
385         field_val = omap2_divisor_to_clksel(clk, new_div);
386         if (field_val == ~0)
387                 return -EINVAL;
388
389         v = __raw_readl(clk->clksel_reg);
390         v &= ~clk->clksel_mask;
391         v |= field_val << __ffs(clk->clksel_mask);
392         __raw_writel(v, clk->clksel_reg);
393         v = __raw_readl(clk->clksel_reg); /* OCP barrier */
394
395         clk->rate = clk->parent->rate / new_div;
396
397         return 0;
398 }
399
400 int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent)
401 {
402         u32 field_val, v, parent_div;
403
404         if (!clk->clksel || !clk->clksel_mask)
405                 return -EINVAL;
406
407         parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val);
408         if (!parent_div)
409                 return -EINVAL;
410
411         /* Set new source value (previous dividers if any in effect) */
412         v = __raw_readl(clk->clksel_reg);
413         v &= ~clk->clksel_mask;
414         v |= field_val << __ffs(clk->clksel_mask);
415         __raw_writel(v, clk->clksel_reg);
416         v = __raw_readl(clk->clksel_reg);    /* OCP barrier */
417
418         clk_reparent(clk, new_parent);
419
420         /* CLKSEL clocks follow their parents' rates, divided by a divisor */
421         clk->rate = new_parent->rate;
422
423         if (parent_div > 0)
424                 clk->rate /= parent_div;
425
426         pr_debug("clock: set parent of %s to %s (new rate %ld)\n",
427                  clk->name, clk->parent->name, clk->rate);
428
429         return 0;
430 }