diff options
author | Paul Walmsley <paul@pwsan.com> | 2010-05-18 20:40:24 -0400 |
---|---|---|
committer | Paul Walmsley <paul@pwsan.com> | 2010-05-20 14:31:06 -0400 |
commit | 435699db6a3d81bfd36d25150177399a4f6048d5 (patch) | |
tree | f6fa03a580c900178ea6b2b0c64e9272460d3cd3 /arch | |
parent | d74b4949714741f4c58cd1801a6a92737b89a61c (diff) |
OMAP2+ clock: clean up clkt_clksel.c
This patch cleans up arch/arm/mach-omap2/clkt_clksel.c. It:
- makes several functions static that are not called outside the file;
- adds documentation;
- makes some code paths easier to read (hopefully), by breaking up
compound statements and removing redundant checks;
- converts some pr_err()s that indicate clock tree data problems into WARN()s,
so they are more likely to be noticed;
- and moves omap2_clk_round_rate() back into mach-omap2/clock.c, its proper
home, since it is not clksel-specific.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/clkt_clksel.c | 423 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.c | 9 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.h | 11 |
3 files changed, 266 insertions, 177 deletions
diff --git a/arch/arm/mach-omap2/clkt_clksel.c b/arch/arm/mach-omap2/clkt_clksel.c index 9a23aebeea7e..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,23 +59,19 @@ | |||
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 | ||
@@ -68,7 +79,7 @@ static const struct clksel *_omap2_get_clksel_by_parent(struct clk *clk, | |||
68 | } | 79 | } |
69 | 80 | ||
70 | /** | 81 | /** |
71 | * _omap2_clksel_get_src_field - find the new clksel divisor to use | 82 | * _get_div_and_fieldval() - find the new clksel divisor and field value to use |
72 | * @src_clk: planned new parent struct clk * | 83 | * @src_clk: planned new parent struct clk * |
73 | * @clk: struct clk * that is being reparented | 84 | * @clk: struct clk * that is being reparented |
74 | * @field_val: pointer to a u32 to contain the register data for the divisor | 85 | * @field_val: pointer to a u32 to contain the register data for the divisor |
@@ -82,14 +93,14 @@ static const struct clksel *_omap2_get_clksel_by_parent(struct clk *clk, | |||
82 | * success (in this latter case, the corresponding register bitfield | 93 | * success (in this latter case, the corresponding register bitfield |
83 | * value is passed back in the variable pointed to by @field_val) | 94 | * value is passed back in the variable pointed to by @field_val) |
84 | */ | 95 | */ |
85 | static u8 _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, |
86 | u32 *field_val) | 97 | u32 *field_val) |
87 | { | 98 | { |
88 | const struct clksel *clks; | 99 | const struct clksel *clks; |
89 | const struct clksel_rate *clkr, *max_clkr; | 100 | const struct clksel_rate *clkr, *max_clkr; |
90 | u8 max_div = 0; | 101 | u8 max_div = 0; |
91 | 102 | ||
92 | clks = _omap2_get_clksel_by_parent(clk, src_clk); | 103 | clks = _get_clksel_by_parent(clk, src_clk); |
93 | if (!clks) | 104 | if (!clks) |
94 | return 0; | 105 | return 0; |
95 | 106 | ||
@@ -113,9 +124,9 @@ static u8 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, | |||
113 | } | 124 | } |
114 | 125 | ||
115 | if (max_div == 0) { | 126 | if (max_div == 0) { |
116 | WARN(1, "clock: Could not find divisor for " | 127 | /* This indicates an error in the clksel data */ |
117 | "clock %s parent %s\n", clk->name, | 128 | WARN(1, "clock: Could not find divisor for clock %s parent %s" |
118 | src_clk->parent->name); | 129 | "\n", clk->name, src_clk->parent->name); |
119 | return 0; | 130 | return 0; |
120 | } | 131 | } |
121 | 132 | ||
@@ -124,82 +135,141 @@ static u8 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, | |||
124 | return max_div; | 135 | return max_div; |
125 | } | 136 | } |
126 | 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; | ||
127 | 154 | ||
128 | /* 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 | } | ||
129 | 162 | ||
130 | /** | 163 | /** |
131 | * 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 |
132 | * @clk: OMAP clock struct ptr to use | 165 | * @clk: OMAP struct clk to use |
166 | * @field_val: register field value to find | ||
133 | * | 167 | * |
134 | * 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 |
135 | * register and determine what its parent is currently set to. Update the | 169 | * value to search for, find the corresponding clock divisor. The register |
136 | * 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. | ||
137 | */ | 173 | */ |
138 | void omap2_init_clksel_parent(struct clk *clk) | 174 | static u32 _clksel_to_divisor(struct clk *clk, u32 field_val) |
139 | { | 175 | { |
140 | const struct clksel *clks; | 176 | const struct clksel *clks; |
141 | const struct clksel_rate *clkr; | 177 | const struct clksel_rate *clkr; |
142 | u32 r, found = 0; | ||
143 | 178 | ||
144 | if (!clk->clksel) | 179 | clks = _get_clksel_by_parent(clk, clk->parent); |
145 | return; | 180 | if (!clks) |
181 | return 0; | ||
146 | 182 | ||
147 | r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; | 183 | for (clkr = clks->rates; clkr->div; clkr++) { |
148 | r >>= __ffs(clk->clksel_mask); | 184 | if (!(clkr->flags & cpu_mask)) |
185 | continue; | ||
149 | 186 | ||
150 | for (clks = clk->clksel; clks->parent && !found; clks++) { | 187 | if (clkr->val == field_val) |
151 | for (clkr = clks->rates; clkr->div && !found; clkr++) { | 188 | break; |
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 | } | 189 | } |
165 | 190 | ||
166 | if (!found) | 191 | if (!clkr->div) { |
167 | printk(KERN_ERR "clock: init parent: could not find " | 192 | /* This indicates a data error */ |
168 | "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 | } | ||
169 | 197 | ||
170 | return; | 198 | return clkr->div; |
171 | } | 199 | } |
172 | 200 | ||
173 | /* | 201 | /** |
174 | * Used for clocks that are part of CLKSEL_xyz governed clocks. | 202 | * _divisor_to_clksel() - turn clksel integer divisor into a field value |
175 | * 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. | ||
176 | */ | 210 | */ |
177 | unsigned long omap2_clksel_recalc(struct clk *clk) | 211 | static u32 _divisor_to_clksel(struct clk *clk, u32 div) |
178 | { | 212 | { |
179 | unsigned long rate; | 213 | const struct clksel *clks; |
180 | u32 div = 0; | 214 | const struct clksel_rate *clkr; |
181 | 215 | ||
182 | pr_debug("clock: recalc'ing clksel clk %s\n", clk->name); | 216 | /* should never happen */ |
217 | WARN_ON(div == 0); | ||
183 | 218 | ||
184 | div = omap2_clksel_get_divisor(clk); | 219 | clks = _get_clksel_by_parent(clk, clk->parent); |
185 | if (div == 0) | 220 | if (!clks) |
186 | return clk->rate; | 221 | return ~0; |
187 | 222 | ||
188 | rate = clk->parent->rate / div; | 223 | for (clkr = clks->rates; clkr->div; clkr++) { |
224 | if (!(clkr->flags & cpu_mask)) | ||
225 | continue; | ||
189 | 226 | ||
190 | pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div); | 227 | if (clkr->div == div) |
228 | break; | ||
229 | } | ||
191 | 230 | ||
192 | 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; | ||
193 | } | 238 | } |
194 | 239 | ||
195 | /** | 240 | /** |
196 | * omap2_clksel_round_rate_div - find divisor for the given clock and rate | 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); | ||
260 | } | ||
261 | |||
262 | /* Public functions */ | ||
263 | |||
264 | /** | ||
265 | * omap2_clksel_round_rate_div() - find divisor for the given clock and rate | ||
197 | * @clk: OMAP struct clk to use | 266 | * @clk: OMAP struct clk to use |
198 | * @target_rate: desired clock rate | 267 | * @target_rate: desired clock rate |
199 | * @new_div: ptr to where we should store the divisor | 268 | * @new_div: ptr to where we should store the divisor |
200 | * | 269 | * |
201 | * 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 |
202 | * rates. The divider array must be sorted with smallest divider first. | 271 | * rates. The divider array must be sorted with smallest divider first. |
272 | * This function is also used by the DPLL3 M2 divider code. | ||
203 | * | 273 | * |
204 | * Returns the rounded clock rate or returns 0xffffffff on error. | 274 | * Returns the rounded clock rate or returns 0xffffffff on error. |
205 | */ | 275 | */ |
@@ -211,12 +281,15 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | |||
211 | const struct clksel_rate *clkr; | 281 | const struct clksel_rate *clkr; |
212 | u32 last_div = 0; | 282 | u32 last_div = 0; |
213 | 283 | ||
284 | if (!clk->clksel || !clk->clksel_mask) | ||
285 | return ~0; | ||
286 | |||
214 | 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", |
215 | clk->name, target_rate); | 288 | clk->name, target_rate); |
216 | 289 | ||
217 | *new_div = 1; | 290 | *new_div = 1; |
218 | 291 | ||
219 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | 292 | clks = _get_clksel_by_parent(clk, clk->parent); |
220 | if (!clks) | 293 | if (!clks) |
221 | return ~0; | 294 | return ~0; |
222 | 295 | ||
@@ -252,168 +325,174 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | |||
252 | return clk->parent->rate / clkr->div; | 325 | return clk->parent->rate / clkr->div; |
253 | } | 326 | } |
254 | 327 | ||
255 | /** | 328 | /* |
256 | * omap2_clksel_round_rate - find rounded rate for the given clock and rate | 329 | * Clocktype interface functions to the OMAP clock code |
257 | * @clk: OMAP struct clk to use | 330 | * (i.e., those used in struct clk field function pointers, etc.) |
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 | */ | 331 | */ |
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 | 332 | ||
285 | /** | 333 | /** |
286 | * 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 |
287 | * @clk: OMAP struct clk to use | 335 | * @clk: OMAP clock struct ptr to use |
288 | * @field_val: register field value to find | ||
289 | * | 336 | * |
290 | * 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 |
291 | * value to search for, find the corresponding clock divisor. The register | 338 | * hardware register and determine what its parent is currently set |
292 | * 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 |
293 | * before calling. Returns 0 on error | 340 | * return value. |
294 | */ | 341 | */ |
295 | u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val) | 342 | void omap2_init_clksel_parent(struct clk *clk) |
296 | { | 343 | { |
297 | const struct clksel *clks; | 344 | const struct clksel *clks; |
298 | const struct clksel_rate *clkr; | 345 | const struct clksel_rate *clkr; |
346 | u32 r, found = 0; | ||
299 | 347 | ||
300 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | 348 | if (!clk->clksel || !clk->clksel_mask) |
301 | if (!clks) | 349 | return; |
302 | return 0; | ||
303 | 350 | ||
304 | for (clkr = clks->rates; clkr->div; clkr++) { | 351 | r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; |
305 | if ((clkr->flags & cpu_mask) && (clkr->val == field_val)) | 352 | r >>= __ffs(clk->clksel_mask); |
306 | break; | ||
307 | } | ||
308 | 353 | ||
309 | if (!clkr->div) { | 354 | for (clks = clk->clksel; clks->parent && !found; clks++) { |
310 | printk(KERN_ERR "clock: Could not find fieldval %d for " | 355 | for (clkr = clks->rates; clkr->div && !found; clkr++) { |
311 | "clock %s parent %s\n", field_val, clk->name, | 356 | if (!(clkr->flags & cpu_mask)) |
312 | clk->parent->name); | 357 | continue; |
313 | 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 | } | ||
314 | } | 371 | } |
315 | 372 | ||
316 | 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; | ||
317 | } | 378 | } |
318 | 379 | ||
319 | /** | 380 | /** |
320 | * 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 |
321 | * @clk: OMAP struct clk to use | 382 | * @clk: struct clk * |
322 | * @div: integer divisor to search for | ||
323 | * | 383 | * |
324 | * 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. |
325 | * 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 |
326 | * 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. | ||
327 | */ | 388 | */ |
328 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div) | 389 | unsigned long omap2_clksel_recalc(struct clk *clk) |
329 | { | 390 | { |
330 | const struct clksel *clks; | 391 | unsigned long rate; |
331 | const struct clksel_rate *clkr; | 392 | u32 div = 0; |
332 | 393 | ||
333 | /* should never happen */ | 394 | div = _read_divisor(clk); |
334 | WARN_ON(div == 0); | 395 | if (div == 0) |
396 | return clk->rate; | ||
335 | 397 | ||
336 | clks = _omap2_get_clksel_by_parent(clk, clk->parent); | 398 | rate = clk->parent->rate / div; |
337 | if (!clks) | ||
338 | return ~0; | ||
339 | 399 | ||
340 | for (clkr = clks->rates; clkr->div; clkr++) { | 400 | pr_debug("clock: %s: recalc'd rate is %ld (div %d)\n", clk->name, |
341 | if ((clkr->flags & cpu_mask) && (clkr->div == div)) | 401 | rate, div); |
342 | break; | ||
343 | } | ||
344 | 402 | ||
345 | if (!clkr->div) { | 403 | return rate; |
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 | } | 404 | } |
354 | 405 | ||
355 | /** | 406 | /** |
356 | * 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 |
357 | * @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. | ||
358 | * | 414 | * |
359 | * Returns the integer divisor upon success or 0 on error. | 415 | * Returns the rounded clock rate or returns 0xffffffff on error. |
360 | */ | 416 | */ |
361 | u32 omap2_clksel_get_divisor(struct clk *clk) | 417 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate) |
362 | { | 418 | { |
363 | u32 v; | 419 | u32 new_div; |
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 | 420 | ||
371 | return omap2_clksel_to_divisor(clk, v); | 421 | return omap2_clksel_round_rate_div(clk, target_rate, &new_div); |
372 | } | 422 | } |
373 | 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 | */ | ||
374 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) | 439 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) |
375 | { | 440 | { |
376 | u32 v, field_val, validrate, new_div = 0; | 441 | u32 field_val, validrate, new_div = 0; |
377 | 442 | ||
378 | if (!clk->clksel_mask) | 443 | if (!clk->clksel || !clk->clksel_mask) |
379 | return -EINVAL; | 444 | return -EINVAL; |
380 | 445 | ||
381 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); | 446 | validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); |
382 | if (validrate != rate) | 447 | if (validrate != rate) |
383 | return -EINVAL; | 448 | return -EINVAL; |
384 | 449 | ||
385 | field_val = omap2_divisor_to_clksel(clk, new_div); | 450 | field_val = _divisor_to_clksel(clk, new_div); |
386 | if (field_val == ~0) | 451 | if (field_val == ~0) |
387 | return -EINVAL; | 452 | return -EINVAL; |
388 | 453 | ||
389 | v = __raw_readl(clk->clksel_reg); | 454 | _write_clksel_reg(clk, field_val); |
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 | 455 | ||
395 | clk->rate = clk->parent->rate / new_div; | 456 | clk->rate = clk->parent->rate / new_div; |
396 | 457 | ||
458 | pr_debug("clock: %s: set rate to %ld\n", clk->name, clk->rate); | ||
459 | |||
397 | return 0; | 460 | return 0; |
398 | } | 461 | } |
399 | 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 | */ | ||
400 | 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) |
401 | { | 484 | { |
402 | u32 field_val, v, parent_div; | 485 | u32 field_val = 0; |
486 | u32 parent_div; | ||
403 | 487 | ||
404 | if (!clk->clksel || !clk->clksel_mask) | 488 | if (!clk->clksel || !clk->clksel_mask) |
405 | return -EINVAL; | 489 | return -EINVAL; |
406 | 490 | ||
407 | parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); | 491 | parent_div = _get_div_and_fieldval(new_parent, clk, &field_val); |
408 | if (!parent_div) | 492 | if (!parent_div) |
409 | return -EINVAL; | 493 | return -EINVAL; |
410 | 494 | ||
411 | /* Set new source value (previous dividers if any in effect) */ | 495 | _write_clksel_reg(clk, field_val); |
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 | 496 | ||
418 | clk_reparent(clk, new_parent); | 497 | clk_reparent(clk, new_parent); |
419 | 498 | ||
@@ -423,7 +502,7 @@ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) | |||
423 | if (parent_div > 0) | 502 | if (parent_div > 0) |
424 | clk->rate /= parent_div; | 503 | clk->rate /= parent_div; |
425 | 504 | ||
426 | 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", |
427 | clk->name, clk->parent->name, clk->rate); | 506 | clk->name, clk->parent->name, clk->rate); |
428 | 507 | ||
429 | return 0; | 508 | return 0; |
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index a6d0b34b7990..605f531783a8 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c | |||
@@ -334,6 +334,15 @@ oce_err1: | |||
334 | return ret; | 334 | return ret; |
335 | } | 335 | } |
336 | 336 | ||
337 | /* Given a clock and a rate apply a clock specific rounding function */ | ||
338 | long omap2_clk_round_rate(struct clk *clk, unsigned long rate) | ||
339 | { | ||
340 | if (clk->round_rate) | ||
341 | return clk->round_rate(clk, rate); | ||
342 | |||
343 | return clk->rate; | ||
344 | } | ||
345 | |||
337 | /* Set the clock rate for a clock source */ | 346 | /* Set the clock rate for a clock source */ |
338 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate) | 347 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate) |
339 | { | 348 | { |
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index ad8a1f7c1afc..a535c7a2a62a 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h | |||
@@ -73,19 +73,20 @@ void omap2_clk_disable_unused(struct clk *clk); | |||
73 | #define omap2_clk_disable_unused NULL | 73 | #define omap2_clk_disable_unused NULL |
74 | #endif | 74 | #endif |
75 | 75 | ||
76 | unsigned long omap2_clksel_recalc(struct clk *clk); | ||
77 | void omap2_init_clk_clkdm(struct clk *clk); | 76 | void omap2_init_clk_clkdm(struct clk *clk); |
78 | void omap2_init_clksel_parent(struct clk *clk); | 77 | |
79 | u32 omap2_clksel_get_divisor(struct clk *clk); | 78 | /* clkt_clksel.c public functions */ |
80 | u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, | 79 | u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, |
81 | u32 *new_div); | 80 | u32 *new_div); |
82 | u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val); | 81 | void omap2_init_clksel_parent(struct clk *clk); |
83 | u32 omap2_divisor_to_clksel(struct clk *clk, u32 div); | 82 | unsigned long omap2_clksel_recalc(struct clk *clk); |
84 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate); | 83 | long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate); |
85 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate); | 84 | int omap2_clksel_set_rate(struct clk *clk, unsigned long rate); |
86 | int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent); | 85 | int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent); |
86 | |||
87 | u32 omap2_get_dpll_rate(struct clk *clk); | 87 | u32 omap2_get_dpll_rate(struct clk *clk); |
88 | void omap2_init_dpll_parent(struct clk *clk); | 88 | void omap2_init_dpll_parent(struct clk *clk); |
89 | |||
89 | int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name); | 90 | int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name); |
90 | 91 | ||
91 | 92 | ||