diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-05-23 19:52:55 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-05-23 19:52:55 -0400 |
commit | 1f782fee18b39b9ad438ebbd82c2915a16c879ee (patch) | |
tree | f292930065e6c860714c134790ab8882680ac739 /arch/arm/mach-omap2/clkt_clksel.c | |
parent | 8eda2f21ed9c936a54fd7bc16cbfa5ee656635c2 (diff) | |
parent | f4b87dee923342505e1ddba8d34ce9de33e75050 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'arch/arm/mach-omap2/clkt_clksel.c')
-rw-r--r-- | arch/arm/mach-omap2/clkt_clksel.c | 472 |
1 files changed, 286 insertions, 186 deletions
diff --git a/arch/arm/mach-omap2/clkt_clksel.c b/arch/arm/mach-omap2/clkt_clksel.c index e50812dd03fd..a781cd6795a4 100644 --- a/arch/arm/mach-omap2/clkt_clksel.c +++ b/arch/arm/mach-omap2/clkt_clksel.c | |||
@@ -12,8 +12,26 @@ | |||
12 | * it under the terms of the GNU General Public License version 2 as | 12 | * it under the terms of the GNU General Public License version 2 as |
13 | * published by the Free Software Foundation. | 13 | * published by the Free Software Foundation. |
14 | * | 14 | * |
15 | * XXX At some point these clksel clocks should be split into | 15 | * |
16 | * "divider" clocks and "mux" clocks to better match the hardware. | 16 | * clksel clocks are clocks that do not have a fixed parent, or that |
17 | * can divide their parent's rate, or possibly both at the same time, based | ||
18 | * on the contents of a hardware register bitfield. | ||
19 | * | ||
20 | * All of the various mux and divider settings can be encoded into | ||
21 | * struct clksel* data structures, and then these can be autogenerated | ||
22 | * from some hardware database for each new chip generation. This | ||
23 | * should avoid the need to write, review, and validate a lot of new | ||
24 | * clock code for each new chip, since it can be exported from the SoC | ||
25 | * design flow. This is now done on OMAP4. | ||
26 | * | ||
27 | * The fusion of mux and divider clocks is a software creation. In | ||
28 | * hardware reality, the multiplexer (parent selection) and the | ||
29 | * divider exist separately. XXX At some point these clksel clocks | ||
30 | * should be split into "divider" clocks and "mux" clocks to better | ||
31 | * match the hardware. | ||
32 | * | ||
33 | * (The name "clksel" comes from the name of the corresponding | ||
34 | * register field in the OMAP2/3 family of SoCs.) | ||
17 | * | 35 | * |
18 | * XXX Currently these clocks are only used in the OMAP2/3/4 code, but | 36 | * 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 | 37 | * many of the OMAP1 clocks should be convertible to use this |
@@ -29,14 +47,11 @@ | |||
29 | #include <plat/clock.h> | 47 | #include <plat/clock.h> |
30 | 48 | ||
31 | #include "clock.h" | 49 | #include "clock.h" |
32 | #include "cm.h" | ||
33 | #include "cm-regbits-24xx.h" | ||
34 | #include "cm-regbits-34xx.h" | ||
35 | 50 | ||
36 | /* Private functions */ | 51 | /* Private functions */ |
37 | 52 | ||
38 | /** | 53 | /** |
39 | * _omap2_get_clksel_by_parent - return clksel struct for a given clk & parent | 54 | * _get_clksel_by_parent() - return clksel struct for a given clk & parent |
40 | * @clk: OMAP struct clk ptr to inspect | 55 | * @clk: OMAP struct clk ptr to inspect |
41 | * @src_clk: OMAP struct clk ptr of the parent clk to search for | 56 | * @src_clk: OMAP struct clk ptr of the parent clk to search for |
42 | * | 57 | * |
@@ -44,141 +59,217 @@ | |||
44 | * the element associated with the supplied parent clock address. | 59 | * the element associated with the supplied parent clock address. |
45 | * Returns a pointer to the struct clksel on success or NULL on error. | 60 | * Returns a pointer to the struct clksel on success or NULL on error. |
46 | */ | 61 | */ |
47 | static const struct clksel *_omap2_get_clksel_by_parent(struct clk *clk, | 62 | static const struct clksel *_get_clksel_by_parent(struct clk *clk, |
48 | struct clk *src_clk) | 63 | struct clk *src_clk) |
49 | { | 64 | { |
50 | const struct clksel *clks; | 65 | const struct clksel *clks; |
51 | 66 | ||
52 | if (!clk->clksel) | 67 | for (clks = clk->clksel; clks->parent; clks++) |
53 | return NULL; | ||
54 | |||
55 | for (clks = clk->clksel; clks->parent; clks++) { | ||
56 | if (clks->parent == src_clk) | 68 | if (clks->parent == src_clk) |
57 | break; /* Found the requested parent */ | 69 | break; /* Found the requested parent */ |
58 | } | ||
59 | 70 | ||
60 | if (!clks->parent) { | 71 | if (!clks->parent) { |
61 | printk(KERN_ERR "clock: Could not find parent clock %s in " | 72 | /* This indicates a data problem */ |
62 | "clksel array of clock %s\n", src_clk->name, | 73 | WARN(1, "clock: Could not find parent clock %s in clksel array " |
63 | clk->name); | 74 | "of clock %s\n", src_clk->name, clk->name); |
64 | return NULL; | 75 | return NULL; |
65 | } | 76 | } |
66 | 77 | ||
67 | return clks; | 78 | return clks; |
68 | } | 79 | } |
69 | 80 | ||
70 | /* | 81 | /** |
71 | * Converts encoded control register address into a full address | 82 | * _get_div_and_fieldval() - find the new clksel divisor and field value to use |
72 | * On error, the return value (parent_div) will be 0. | 83 | * @src_clk: planned new parent struct clk * |
84 | * @clk: struct clk * that is being reparented | ||
85 | * @field_val: pointer to a u32 to contain the register data for the divisor | ||
86 | * | ||
87 | * Given an intended new parent struct clk * @src_clk, and the struct | ||
88 | * clk * @clk to the clock that is being reparented, find the | ||
89 | * appropriate rate divisor for the new clock (returned as the return | ||
90 | * value), and the corresponding register bitfield data to program to | ||
91 | * reach that divisor (returned in the u32 pointed to by @field_val). | ||
92 | * Returns 0 on error, or returns the newly-selected divisor upon | ||
93 | * success (in this latter case, the corresponding register bitfield | ||
94 | * value is passed back in the variable pointed to by @field_val) | ||
73 | */ | 95 | */ |
74 | static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, | 96 | static u8 _get_div_and_fieldval(struct clk *src_clk, struct clk *clk, |
75 | u32 *field_val) | 97 | u32 *field_val) |
76 | { | 98 | { |
77 | const struct clksel *clks; | 99 | const struct clksel *clks; |
78 | const struct clksel_rate *clkr; | 100 | const struct clksel_rate *clkr, *max_clkr; |
101 | u8 max_div = 0; | ||
79 | 102 | ||
80 | clks = _omap2_get_clksel_by_parent(clk, src_clk); | 103 | clks = _get_clksel_by_parent(clk, src_clk); |
81 | if (!clks) | 104 | if (!clks) |
82 | return 0; | 105 | return 0; |
83 | 106 | ||
107 | /* | ||
108 | * Find the highest divisor (e.g., the one resulting in the | ||
109 | * lowest rate) to use as the default. This should avoid | ||
110 | * clock rates that are too high for the device. XXX A better | ||
111 | * solution here would be to try to determine if there is a | ||
112 | * divisor matching the original clock rate before the parent | ||
113 | * switch, and if it cannot be found, to fall back to the | ||
114 | * highest divisor. | ||
115 | */ | ||
84 | for (clkr = clks->rates; clkr->div; clkr++) { | 116 | for (clkr = clks->rates; clkr->div; clkr++) { |
85 | if (clkr->flags & cpu_mask && clkr->flags & DEFAULT_RATE) | 117 | if (!(clkr->flags & cpu_mask)) |
86 | break; /* Found the default rate for this platform */ | 118 | continue; |
119 | |||
120 | if (clkr->div > max_div) { | ||
121 | max_div = clkr->div; | ||
122 | max_clkr = clkr; | ||
123 | } | ||
87 | } | 124 | } |
88 | 125 | ||
89 | if (!clkr->div) { | 126 | if (max_div == 0) { |
90 | printk(KERN_ERR "clock: Could not find default rate for " | 127 | /* This indicates an error in the clksel data */ |
91 | "clock %s parent %s\n", clk->name, | 128 | WARN(1, "clock: Could not find divisor for clock %s parent %s" |
92 | src_clk->parent->name); | 129 | "\n", clk->name, src_clk->parent->name); |
93 | return 0; | 130 | return 0; |
94 | } | 131 | } |
95 | 132 | ||
96 | /* Should never happen. Add a clksel mask to the struct clk. */ | 133 | *field_val = max_clkr->val; |
97 | WARN_ON(clk->clksel_mask == 0); | ||
98 | 134 | ||
99 | *field_val = clkr->val; | 135 | return max_div; |
100 | |||
101 | return clkr->div; | ||
102 | } | 136 | } |
103 | 137 | ||
138 | /** | ||
139 | * _write_clksel_reg() - program a clock's clksel register in hardware | ||
140 | * @clk: struct clk * to program | ||
141 | * @v: clksel bitfield value to program (with LSB at bit 0) | ||
142 | * | ||
143 | * Shift the clksel register bitfield value @v to its appropriate | ||
144 | * location in the clksel register and write it in. This function | ||
145 | * will ensure that the write to the clksel_reg reaches its | ||
146 | * destination before returning -- important since PRM and CM register | ||
147 | * accesses can be quite slow compared to ARM cycles -- but does not | ||
148 | * take into account any time the hardware might take to switch the | ||
149 | * clock source. | ||
150 | */ | ||
151 | static void _write_clksel_reg(struct clk *clk, u32 field_val) | ||
152 | { | ||
153 | u32 v; | ||
104 | 154 | ||
105 | /* Public functions */ | 155 | v = __raw_readl(clk->clksel_reg); |
156 | v &= ~clk->clksel_mask; | ||
157 | v |= field_val << __ffs(clk->clksel_mask); | ||
158 | __raw_writel(v, clk->clksel_reg); | ||
159 | |||
160 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ | ||
161 | } | ||
106 | 162 | ||
107 | /** | 163 | /** |
108 | * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware | 164 | * _clksel_to_divisor() - turn clksel field value into integer divider |
109 | * @clk: OMAP clock struct ptr to use | 165 | * @clk: OMAP struct clk to use |
166 | * @field_val: register field value to find | ||
110 | * | 167 | * |
111 | * Given a pointer to a source-selectable struct clk, read the hardware | 168 | * Given a struct clk of a rate-selectable clksel clock, and a register field |
112 | * register and determine what its parent is currently set to. Update the | 169 | * value to search for, find the corresponding clock divisor. The register |
113 | * clk->parent field with the appropriate clk ptr. | 170 | * field value should be pre-masked and shifted down so the LSB is at bit 0 |
171 | * before calling. Returns 0 on error or returns the actual integer divisor | ||
172 | * upon success. | ||
114 | */ | 173 | */ |
115 | void omap2_init_clksel_parent(struct clk *clk) | 174 | static u32 _clksel_to_divisor(struct clk *clk, u32 field_val) |
116 | { | 175 | { |
117 | const struct clksel *clks; | 176 | const struct clksel *clks; |
118 | const struct clksel_rate *clkr; | 177 | const struct clksel_rate *clkr; |
119 | u32 r, found = 0; | ||
120 | 178 | ||
121 | if (!clk->clksel) | 179 | clks = _get_clksel_by_parent(clk, clk->parent); |
122 | return; | 180 | if (!clks) |
181 | return 0; | ||
123 | 182 | ||
124 | r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | 183 | for (clkr = clks->rates; clkr->div; clkr++) { |
125 | r >>= __ffs(clk->clksel_mask); | 184 | if (!(clkr->flags & cpu_mask)) |
185 | continue; | ||
126 | 186 | ||
127 | for (clks = clk->clksel; clks->parent && !found; clks++) { | 187 | if (clkr->val == field_val) |
128 | for (clkr = clks->rates; clkr->div && !found; clkr++) { | 188 | break; |
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 | } | 189 | } |
142 | 190 | ||
143 | if (!found) | 191 | if (!clkr->div) { |
144 | printk(KERN_ERR "clock: init parent: could not find " | 192 | /* This indicates a data error */ |
145 | "regval %0x for clock %s\n", r, clk->name); | 193 | WARN(1, "clock: Could not find fieldval %d for clock %s parent " |
194 | "%s\n", field_val, clk->name, clk->parent->name); | ||
195 | return 0; | ||
196 | } | ||
146 | 197 | ||
147 | return; | 198 | return clkr->div; |
148 | } | 199 | } |
149 | 200 | ||
150 | /* | 201 | /** |
151 | * Used for clocks that are part of CLKSEL_xyz governed clocks. | 202 | * _divisor_to_clksel() - turn clksel integer divisor into a field value |
152 | * REVISIT: Maybe change to use clk->enable() functions like on omap1? | 203 | * @clk: OMAP struct clk to use |
204 | * @div: integer divisor to search for | ||
205 | * | ||
206 | * Given a struct clk of a rate-selectable clksel clock, and a clock | ||
207 | * divisor, find the corresponding register field value. Returns the | ||
208 | * register field value _before_ left-shifting (i.e., LSB is at bit | ||
209 | * 0); or returns 0xFFFFFFFF (~0) upon error. | ||
153 | */ | 210 | */ |
154 | unsigned long omap2_clksel_recalc(struct clk *clk) | 211 | static u32 _divisor_to_clksel(struct clk *clk, u32 div) |
155 | { | 212 | { |
156 | unsigned long rate; | 213 | const struct clksel *clks; |
157 | u32 div = 0; | 214 | const struct clksel_rate *clkr; |
158 | 215 | ||
159 | pr_debug("clock: recalc'ing clksel clk %s\n", clk->name); | 216 | /* should never happen */ |
217 | WARN_ON(div == 0); | ||
160 | 218 | ||
161 | div = omap2_clksel_get_divisor(clk); | 219 | clks = _get_clksel_by_parent(clk, clk->parent); |
162 | if (div == 0) | 220 | if (!clks) |
163 | return clk->rate; | 221 | return ~0; |
164 | 222 | ||
165 | rate = clk->parent->rate / div; | 223 | for (clkr = clks->rates; clkr->div; clkr++) { |
224 | if (!(clkr->flags & cpu_mask)) | ||
225 | continue; | ||
166 | 226 | ||
167 | pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div); | 227 | if (clkr->div == div) |
228 | break; | ||
229 | } | ||
168 | 230 | ||
169 | return rate; | 231 | if (!clkr->div) { |
232 | pr_err("clock: Could not find divisor %d for clock %s parent " | ||
233 | "%s\n", div, clk->name, clk->parent->name); | ||
234 | return ~0; | ||
235 | } | ||
236 | |||
237 | return clkr->val; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * _read_divisor() - get current divisor applied to parent clock (from hdwr) | ||
242 | * @clk: OMAP struct clk to use. | ||
243 | * | ||
244 | * Read the current divisor register value for @clk that is programmed | ||
245 | * into the hardware, convert it into the actual divisor value, and | ||
246 | * return it; or return 0 on error. | ||
247 | */ | ||
248 | static u32 _read_divisor(struct clk *clk) | ||
249 | { | ||
250 | u32 v; | ||
251 | |||
252 | if (!clk->clksel || !clk->clksel_mask) | ||
253 | return 0; | ||
254 | |||
255 | v = __raw_readl(clk->clksel_reg); | ||
256 | v &= clk->clksel_mask; | ||
257 | v >>= __ffs(clk->clksel_mask); | ||
258 | |||
259 | return _clksel_to_divisor(clk, v); | ||
170 | } | 260 | } |
171 | 261 | ||
262 | /* Public functions */ | ||
263 | |||
172 | /** | 264 | /** |
173 | * omap2_clksel_round_rate_div - find divisor for the given clock and rate | 265 | * omap2_clksel_round_rate_div() - find divisor for the given clock and rate |
174 | * @clk: OMAP struct clk to use | 266 | * @clk: OMAP struct clk to use |
175 | * @target_rate: desired clock rate | 267 | * @target_rate: desired clock rate |
176 | * @new_div: ptr to where we should store the divisor | 268 | * @new_div: ptr to where we should store the divisor |
177 | * | 269 | * |
178 | * Finds 'best' divider value in an array based on the source and target | 270 | * 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. | 271 | * 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, | 272 | * This function is also used by the DPLL3 M2 divider code. |
181 | * they are only settable as part of virtual_prcm set. | ||
182 | * | 273 | * |
183 | * Returns the rounded clock rate or returns 0xffffffff on error. | 274 | * Returns the rounded clock rate or returns 0xffffffff on error. |
184 | */ | 275 | */ |
@@ -190,12 +281,15 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | |||
190 | const struct clksel_rate *clkr; | 281 | const struct clksel_rate *clkr; |
191 | u32 last_div = 0; | 282 | u32 last_div = 0; |
192 | 283 | ||
284 | if (!clk->clksel || !clk->clksel_mask) | ||
285 | return ~0; | ||
286 | |||
193 | pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n", | 287 | pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n", |
194 | clk->name, target_rate); | 288 | clk->name, target_rate); |
195 | 289 | ||
196 | *new_div = 1; | 290 | *new_div = 1; |
197 | 291 | ||
198 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | 292 | clks = _get_clksel_by_parent(clk, clk->parent); |
199 | if (!clks) | 293 | if (!clks) |
200 | return ~0; | 294 | return ~0; |
201 | 295 | ||
@@ -231,168 +325,174 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | |||
231 | return clk->parent->rate / clkr->div; | 325 | return clk->parent->rate / clkr->div; |
232 | } | 326 | } |
233 | 327 | ||
234 | /** | 328 | /* |
235 | * omap2_clksel_round_rate - find rounded rate for the given clock and rate | 329 | * Clocktype interface functions to the OMAP clock code |
236 | * @clk: OMAP struct clk to use | 330 | * (i.e., those used in struct clk field function pointers, etc.) |
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 | */ | 331 | */ |
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 | return clk->rate; | ||
262 | } | ||
263 | 332 | ||
264 | /** | 333 | /** |
265 | * omap2_clksel_to_divisor() - turn clksel field value into integer divider | 334 | * omap2_init_clksel_parent() - set a clksel clk's parent field from the hdwr |
266 | * @clk: OMAP struct clk to use | 335 | * @clk: OMAP clock struct ptr to use |
267 | * @field_val: register field value to find | ||
268 | * | 336 | * |
269 | * Given a struct clk of a rate-selectable clksel clock, and a register field | 337 | * Given a pointer @clk to a source-selectable struct clk, read the |
270 | * value to search for, find the corresponding clock divisor. The register | 338 | * hardware register and determine what its parent is currently set |
271 | * field value should be pre-masked and shifted down so the LSB is at bit 0 | 339 | * to. Update @clk's .parent field with the appropriate clk ptr. No |
272 | * before calling. Returns 0 on error | 340 | * return value. |
273 | */ | 341 | */ |
274 | u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val) | 342 | void omap2_init_clksel_parent(struct clk *clk) |
275 | { | 343 | { |
276 | const struct clksel *clks; | 344 | const struct clksel *clks; |
277 | const struct clksel_rate *clkr; | 345 | const struct clksel_rate *clkr; |
346 | u32 r, found = 0; | ||
278 | 347 | ||
279 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | 348 | if (!clk->clksel || !clk->clksel_mask) |
280 | if (!clks) | 349 | return; |
281 | return 0; | ||
282 | 350 | ||
283 | for (clkr = clks->rates; clkr->div; clkr++) { | 351 | r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; |
284 | if ((clkr->flags & cpu_mask) && (clkr->val == field_val)) | 352 | r >>= __ffs(clk->clksel_mask); |
285 | break; | ||
286 | } | ||
287 | 353 | ||
288 | if (!clkr->div) { | 354 | for (clks = clk->clksel; clks->parent && !found; clks++) { |
289 | printk(KERN_ERR "clock: Could not find fieldval %d for " | 355 | for (clkr = clks->rates; clkr->div && !found; clkr++) { |
290 | "clock %s parent %s\n", field_val, clk->name, | 356 | if (!(clkr->flags & cpu_mask)) |
291 | clk->parent->name); | 357 | continue; |
292 | return 0; | 358 | |
359 | if (clkr->val == r) { | ||
360 | if (clk->parent != clks->parent) { | ||
361 | pr_debug("clock: inited %s parent " | ||
362 | "to %s (was %s)\n", | ||
363 | clk->name, clks->parent->name, | ||
364 | ((clk->parent) ? | ||
365 | clk->parent->name : "NULL")); | ||
366 | clk_reparent(clk, clks->parent); | ||
367 | }; | ||
368 | found = 1; | ||
369 | } | ||
370 | } | ||
293 | } | 371 | } |
294 | 372 | ||
295 | return clkr->div; | 373 | /* This indicates a data error */ |
374 | WARN(!found, "clock: %s: init parent: could not find regval %0x\n", | ||
375 | clk->name, r); | ||
376 | |||
377 | return; | ||
296 | } | 378 | } |
297 | 379 | ||
298 | /** | 380 | /** |
299 | * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value | 381 | * omap2_clksel_recalc() - function ptr to pass via struct clk .recalc field |
300 | * @clk: OMAP struct clk to use | 382 | * @clk: struct clk * |
301 | * @div: integer divisor to search for | ||
302 | * | 383 | * |
303 | * Given a struct clk of a rate-selectable clksel clock, and a clock divisor, | 384 | * This function is intended to be called only by the clock framework. |
304 | * find the corresponding register field value. The return register value is | 385 | * Each clksel clock should have its struct clk .recalc field set to this |
305 | * the value before left-shifting. Returns ~0 on error | 386 | * function. Returns the clock's current rate, based on its parent's rate |
387 | * and its current divisor setting in the hardware. | ||
306 | */ | 388 | */ |
307 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div) | 389 | unsigned long omap2_clksel_recalc(struct clk *clk) |
308 | { | 390 | { |
309 | const struct clksel *clks; | 391 | unsigned long rate; |
310 | const struct clksel_rate *clkr; | 392 | u32 div = 0; |
311 | |||
312 | /* should never happen */ | ||
313 | WARN_ON(div == 0); | ||
314 | 393 | ||
315 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | 394 | div = _read_divisor(clk); |
316 | if (!clks) | 395 | if (div == 0) |
317 | return ~0; | 396 | return clk->rate; |
318 | 397 | ||
319 | for (clkr = clks->rates; clkr->div; clkr++) { | 398 | rate = clk->parent->rate / div; |
320 | if ((clkr->flags & cpu_mask) && (clkr->div == div)) | ||
321 | break; | ||
322 | } | ||
323 | 399 | ||
324 | if (!clkr->div) { | 400 | pr_debug("clock: %s: recalc'd rate is %ld (div %d)\n", clk->name, |
325 | printk(KERN_ERR "clock: Could not find divisor %d for " | 401 | rate, div); |
326 | "clock %s parent %s\n", div, clk->name, | ||
327 | clk->parent->name); | ||
328 | return ~0; | ||
329 | } | ||
330 | 402 | ||
331 | return clkr->val; | 403 | return rate; |
332 | } | 404 | } |
333 | 405 | ||
334 | /** | 406 | /** |
335 | * omap2_clksel_get_divisor - get current divider applied to parent clock. | 407 | * omap2_clksel_round_rate() - find rounded rate for the given clock and rate |
336 | * @clk: OMAP struct clk to use. | 408 | * @clk: OMAP struct clk to use |
409 | * @target_rate: desired clock rate | ||
410 | * | ||
411 | * This function is intended to be called only by the clock framework. | ||
412 | * Finds best target rate based on the source clock and possible dividers. | ||
413 | * rates. The divider array must be sorted with smallest divider first. | ||
337 | * | 414 | * |
338 | * Returns the integer divisor upon success or 0 on error. | 415 | * Returns the rounded clock rate or returns 0xffffffff on error. |
339 | */ | 416 | */ |
340 | u32 omap2_clksel_get_divisor(struct clk *clk) | 417 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate) |
341 | { | 418 | { |
342 | u32 v; | 419 | u32 new_div; |
343 | |||
344 | if (!clk->clksel_mask) | ||
345 | return 0; | ||
346 | |||
347 | v = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | ||
348 | v >>= __ffs(clk->clksel_mask); | ||
349 | 420 | ||
350 | return omap2_clksel_to_divisor(clk, v); | 421 | return omap2_clksel_round_rate_div(clk, target_rate, &new_div); |
351 | } | 422 | } |
352 | 423 | ||
424 | /** | ||
425 | * omap2_clksel_set_rate() - program clock rate in hardware | ||
426 | * @clk: struct clk * to program rate | ||
427 | * @rate: target rate to program | ||
428 | * | ||
429 | * This function is intended to be called only by the clock framework. | ||
430 | * Program @clk's rate to @rate in the hardware. The clock can be | ||
431 | * either enabled or disabled when this happens, although if the clock | ||
432 | * is enabled, some downstream devices may glitch or behave | ||
433 | * unpredictably when the clock rate is changed - this depends on the | ||
434 | * hardware. This function does not currently check the usecount of | ||
435 | * the clock, so if multiple drivers are using the clock, and the rate | ||
436 | * is changed, they will all be affected without any notification. | ||
437 | * Returns -EINVAL upon error, or 0 upon success. | ||
438 | */ | ||
353 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) | 439 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) |
354 | { | 440 | { |
355 | u32 v, field_val, validrate, new_div = 0; | 441 | u32 field_val, validrate, new_div = 0; |
356 | 442 | ||
357 | if (!clk->clksel_mask) | 443 | if (!clk->clksel || !clk->clksel_mask) |
358 | return -EINVAL; | 444 | return -EINVAL; |
359 | 445 | ||
360 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); | 446 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); |
361 | if (validrate != rate) | 447 | if (validrate != rate) |
362 | return -EINVAL; | 448 | return -EINVAL; |
363 | 449 | ||
364 | field_val = omap2_divisor_to_clksel(clk, new_div); | 450 | field_val = _divisor_to_clksel(clk, new_div); |
365 | if (field_val == ~0) | 451 | if (field_val == ~0) |
366 | return -EINVAL; | 452 | return -EINVAL; |
367 | 453 | ||
368 | v = __raw_readl(clk->clksel_reg); | 454 | _write_clksel_reg(clk, field_val); |
369 | v &= ~clk->clksel_mask; | ||
370 | v |= field_val << __ffs(clk->clksel_mask); | ||
371 | __raw_writel(v, clk->clksel_reg); | ||
372 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ | ||
373 | 455 | ||
374 | clk->rate = clk->parent->rate / new_div; | 456 | clk->rate = clk->parent->rate / new_div; |
375 | 457 | ||
458 | pr_debug("clock: %s: set rate to %ld\n", clk->name, clk->rate); | ||
459 | |||
376 | return 0; | 460 | return 0; |
377 | } | 461 | } |
378 | 462 | ||
463 | /* | ||
464 | * Clksel parent setting function - not passed in struct clk function | ||
465 | * pointer - instead, the OMAP clock code currently assumes that any | ||
466 | * parent-setting clock is a clksel clock, and calls | ||
467 | * omap2_clksel_set_parent() by default | ||
468 | */ | ||
469 | |||
470 | /** | ||
471 | * omap2_clksel_set_parent() - change a clock's parent clock | ||
472 | * @clk: struct clk * of the child clock | ||
473 | * @new_parent: struct clk * of the new parent clock | ||
474 | * | ||
475 | * This function is intended to be called only by the clock framework. | ||
476 | * Change the parent clock of clock @clk to @new_parent. This is | ||
477 | * intended to be used while @clk is disabled. This function does not | ||
478 | * currently check the usecount of the clock, so if multiple drivers | ||
479 | * are using the clock, and the parent is changed, they will all be | ||
480 | * affected without any notification. Returns -EINVAL upon error, or | ||
481 | * 0 upon success. | ||
482 | */ | ||
379 | int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) | 483 | int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) |
380 | { | 484 | { |
381 | u32 field_val, v, parent_div; | 485 | u32 field_val = 0; |
486 | u32 parent_div; | ||
382 | 487 | ||
383 | if (!clk->clksel) | 488 | if (!clk->clksel || !clk->clksel_mask) |
384 | return -EINVAL; | 489 | return -EINVAL; |
385 | 490 | ||
386 | parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); | 491 | parent_div = _get_div_and_fieldval(new_parent, clk, &field_val); |
387 | if (!parent_div) | 492 | if (!parent_div) |
388 | return -EINVAL; | 493 | return -EINVAL; |
389 | 494 | ||
390 | /* Set new source value (previous dividers if any in effect) */ | 495 | _write_clksel_reg(clk, field_val); |
391 | v = __raw_readl(clk->clksel_reg); | ||
392 | v &= ~clk->clksel_mask; | ||
393 | v |= field_val << __ffs(clk->clksel_mask); | ||
394 | __raw_writel(v, clk->clksel_reg); | ||
395 | v = __raw_readl(clk->clksel_reg); /* OCP barrier */ | ||
396 | 496 | ||
397 | clk_reparent(clk, new_parent); | 497 | clk_reparent(clk, new_parent); |
398 | 498 | ||
@@ -402,7 +502,7 @@ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) | |||
402 | if (parent_div > 0) | 502 | if (parent_div > 0) |
403 | clk->rate /= parent_div; | 503 | clk->rate /= parent_div; |
404 | 504 | ||
405 | pr_debug("clock: set parent of %s to %s (new rate %ld)\n", | 505 | pr_debug("clock: %s: set parent to %s (new rate %ld)\n", |
406 | clk->name, clk->parent->name, clk->rate); | 506 | clk->name, clk->parent->name, clk->rate); |
407 | 507 | ||
408 | return 0; | 508 | return 0; |