1 | /* |
2 | * Helper routines for SuperH Clock Pulse Generator blocks (CPG). |
3 | * |
4 | * Copyright (C) 2010 Magnus Damm |
5 | * Copyright (C) 2010 - 2012 Paul Mundt |
6 | * |
7 | * This file is subject to the terms and conditions of the GNU General Public |
8 | * License. See the file "COPYING" in the main directory of this archive |
9 | * for more details. |
10 | */ |
11 | #include <linux/clk.h> |
12 | #include <linux/compiler.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/io.h> |
15 | #include <linux/sh_clk.h> |
16 | |
17 | #define CPG_CKSTP_BIT BIT(8) |
18 | |
19 | static unsigned int sh_clk_read(struct clk *clk) |
20 | { |
21 | if (clk->flags & CLK_ENABLE_REG_8BIT) |
22 | return ioread8(clk->mapped_reg); |
23 | else if (clk->flags & CLK_ENABLE_REG_16BIT) |
24 | return ioread16(clk->mapped_reg); |
25 | |
26 | return ioread32(clk->mapped_reg); |
27 | } |
28 | |
29 | static void sh_clk_write(int value, struct clk *clk) |
30 | { |
31 | if (clk->flags & CLK_ENABLE_REG_8BIT) |
32 | iowrite8(value, clk->mapped_reg); |
33 | else if (clk->flags & CLK_ENABLE_REG_16BIT) |
34 | iowrite16(value, clk->mapped_reg); |
35 | else |
36 | iowrite32(value, clk->mapped_reg); |
37 | } |
38 | |
39 | static int sh_clk_mstp_enable(struct clk *clk) |
40 | { |
41 | sh_clk_write(value: sh_clk_read(clk) & ~(1 << clk->enable_bit), clk); |
42 | if (clk->status_reg) { |
43 | unsigned int (*read)(const void __iomem *addr); |
44 | int i; |
45 | void __iomem *mapped_status = (phys_addr_t)clk->status_reg - |
46 | (phys_addr_t)clk->enable_reg + clk->mapped_reg; |
47 | |
48 | if (clk->flags & CLK_ENABLE_REG_8BIT) |
49 | read = ioread8; |
50 | else if (clk->flags & CLK_ENABLE_REG_16BIT) |
51 | read = ioread16; |
52 | else |
53 | read = ioread32; |
54 | |
55 | for (i = 1000; |
56 | (read(mapped_status) & (1 << clk->enable_bit)) && i; |
57 | i--) |
58 | cpu_relax(); |
59 | if (!i) { |
60 | pr_err("cpg: failed to enable %p[%d]\n" , |
61 | clk->enable_reg, clk->enable_bit); |
62 | return -ETIMEDOUT; |
63 | } |
64 | } |
65 | return 0; |
66 | } |
67 | |
68 | static void sh_clk_mstp_disable(struct clk *clk) |
69 | { |
70 | sh_clk_write(value: sh_clk_read(clk) | (1 << clk->enable_bit), clk); |
71 | } |
72 | |
73 | static struct sh_clk_ops sh_clk_mstp_clk_ops = { |
74 | .enable = sh_clk_mstp_enable, |
75 | .disable = sh_clk_mstp_disable, |
76 | .recalc = followparent_recalc, |
77 | }; |
78 | |
79 | int __init sh_clk_mstp_register(struct clk *clks, int nr) |
80 | { |
81 | struct clk *clkp; |
82 | int ret = 0; |
83 | int k; |
84 | |
85 | for (k = 0; !ret && (k < nr); k++) { |
86 | clkp = clks + k; |
87 | clkp->ops = &sh_clk_mstp_clk_ops; |
88 | ret |= clk_register(clkp); |
89 | } |
90 | |
91 | return ret; |
92 | } |
93 | |
94 | /* |
95 | * Div/mult table lookup helpers |
96 | */ |
97 | static inline struct clk_div_table *clk_to_div_table(struct clk *clk) |
98 | { |
99 | return clk->priv; |
100 | } |
101 | |
102 | static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) |
103 | { |
104 | return clk_to_div_table(clk)->div_mult_table; |
105 | } |
106 | |
107 | /* |
108 | * Common div ops |
109 | */ |
110 | static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) |
111 | { |
112 | return clk_rate_table_round(clk, freq_table: clk->freq_table, rate); |
113 | } |
114 | |
115 | static unsigned long sh_clk_div_recalc(struct clk *clk) |
116 | { |
117 | struct clk_div_mult_table *table = clk_to_div_mult_table(clk); |
118 | unsigned int idx; |
119 | |
120 | clk_rate_table_build(clk, freq_table: clk->freq_table, nr_freqs: table->nr_divisors, |
121 | src_table: table, bitmap: clk->arch_flags ? &clk->arch_flags : NULL); |
122 | |
123 | idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; |
124 | |
125 | return clk->freq_table[idx].frequency; |
126 | } |
127 | |
128 | static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) |
129 | { |
130 | struct clk_div_table *dt = clk_to_div_table(clk); |
131 | unsigned long value; |
132 | int idx; |
133 | |
134 | idx = clk_rate_table_find(clk, freq_table: clk->freq_table, rate); |
135 | if (idx < 0) |
136 | return idx; |
137 | |
138 | value = sh_clk_read(clk); |
139 | value &= ~(clk->div_mask << clk->enable_bit); |
140 | value |= (idx << clk->enable_bit); |
141 | sh_clk_write(value, clk); |
142 | |
143 | /* XXX: Should use a post-change notifier */ |
144 | if (dt->kick) |
145 | dt->kick(clk); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static int sh_clk_div_enable(struct clk *clk) |
151 | { |
152 | if (clk->div_mask == SH_CLK_DIV6_MSK) { |
153 | int ret = sh_clk_div_set_rate(clk, rate: clk->rate); |
154 | if (ret < 0) |
155 | return ret; |
156 | } |
157 | |
158 | sh_clk_write(value: sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); |
159 | return 0; |
160 | } |
161 | |
162 | static void sh_clk_div_disable(struct clk *clk) |
163 | { |
164 | unsigned int val; |
165 | |
166 | val = sh_clk_read(clk); |
167 | val |= CPG_CKSTP_BIT; |
168 | |
169 | /* |
170 | * div6 clocks require the divisor field to be non-zero or the |
171 | * above CKSTP toggle silently fails. Ensure that the divisor |
172 | * array is reset to its initial state on disable. |
173 | */ |
174 | if (clk->flags & CLK_MASK_DIV_ON_DISABLE) |
175 | val |= clk->div_mask; |
176 | |
177 | sh_clk_write(value: val, clk); |
178 | } |
179 | |
180 | static struct sh_clk_ops sh_clk_div_clk_ops = { |
181 | .recalc = sh_clk_div_recalc, |
182 | .set_rate = sh_clk_div_set_rate, |
183 | .round_rate = sh_clk_div_round_rate, |
184 | }; |
185 | |
186 | static struct sh_clk_ops sh_clk_div_enable_clk_ops = { |
187 | .recalc = sh_clk_div_recalc, |
188 | .set_rate = sh_clk_div_set_rate, |
189 | .round_rate = sh_clk_div_round_rate, |
190 | .enable = sh_clk_div_enable, |
191 | .disable = sh_clk_div_disable, |
192 | }; |
193 | |
194 | static int __init sh_clk_init_parent(struct clk *clk) |
195 | { |
196 | u32 val; |
197 | |
198 | if (clk->parent) |
199 | return 0; |
200 | |
201 | if (!clk->parent_table || !clk->parent_num) |
202 | return 0; |
203 | |
204 | if (!clk->src_width) { |
205 | pr_err("sh_clk_init_parent: cannot select parent clock\n" ); |
206 | return -EINVAL; |
207 | } |
208 | |
209 | val = (sh_clk_read(clk) >> clk->src_shift); |
210 | val &= (1 << clk->src_width) - 1; |
211 | |
212 | if (val >= clk->parent_num) { |
213 | pr_err("sh_clk_init_parent: parent table size failed\n" ); |
214 | return -EINVAL; |
215 | } |
216 | |
217 | clk_reparent(child: clk, parent: clk->parent_table[val]); |
218 | if (!clk->parent) { |
219 | pr_err("sh_clk_init_parent: unable to set parent" ); |
220 | return -EINVAL; |
221 | } |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | static int __init sh_clk_div_register_ops(struct clk *clks, int nr, |
227 | struct clk_div_table *table, struct sh_clk_ops *ops) |
228 | { |
229 | struct clk *clkp; |
230 | void *freq_table; |
231 | int nr_divs = table->div_mult_table->nr_divisors; |
232 | int freq_table_size = sizeof(struct cpufreq_frequency_table); |
233 | int ret = 0; |
234 | int k; |
235 | |
236 | freq_table_size *= (nr_divs + 1); |
237 | freq_table = kcalloc(n: nr, size: freq_table_size, GFP_KERNEL); |
238 | if (!freq_table) { |
239 | pr_err("%s: unable to alloc memory\n" , __func__); |
240 | return -ENOMEM; |
241 | } |
242 | |
243 | for (k = 0; !ret && (k < nr); k++) { |
244 | clkp = clks + k; |
245 | |
246 | clkp->ops = ops; |
247 | clkp->priv = table; |
248 | |
249 | clkp->freq_table = freq_table + (k * freq_table_size); |
250 | clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; |
251 | |
252 | ret = clk_register(clkp); |
253 | if (ret == 0) |
254 | ret = sh_clk_init_parent(clk: clkp); |
255 | } |
256 | |
257 | return ret; |
258 | } |
259 | |
260 | /* |
261 | * div6 support |
262 | */ |
263 | static int sh_clk_div6_divisors[64] = { |
264 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, |
265 | 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, |
266 | 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, |
267 | 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 |
268 | }; |
269 | |
270 | static struct clk_div_mult_table div6_div_mult_table = { |
271 | .divisors = sh_clk_div6_divisors, |
272 | .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), |
273 | }; |
274 | |
275 | static struct clk_div_table sh_clk_div6_table = { |
276 | .div_mult_table = &div6_div_mult_table, |
277 | }; |
278 | |
279 | static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) |
280 | { |
281 | struct clk_div_mult_table *table = clk_to_div_mult_table(clk); |
282 | u32 value; |
283 | int ret, i; |
284 | |
285 | if (!clk->parent_table || !clk->parent_num) |
286 | return -EINVAL; |
287 | |
288 | /* Search the parent */ |
289 | for (i = 0; i < clk->parent_num; i++) |
290 | if (clk->parent_table[i] == parent) |
291 | break; |
292 | |
293 | if (i == clk->parent_num) |
294 | return -ENODEV; |
295 | |
296 | ret = clk_reparent(child: clk, parent); |
297 | if (ret < 0) |
298 | return ret; |
299 | |
300 | value = sh_clk_read(clk) & |
301 | ~(((1 << clk->src_width) - 1) << clk->src_shift); |
302 | |
303 | sh_clk_write(value: value | (i << clk->src_shift), clk); |
304 | |
305 | /* Rebuild the frequency table */ |
306 | clk_rate_table_build(clk, freq_table: clk->freq_table, nr_freqs: table->nr_divisors, |
307 | src_table: table, NULL); |
308 | |
309 | return 0; |
310 | } |
311 | |
312 | static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { |
313 | .recalc = sh_clk_div_recalc, |
314 | .round_rate = sh_clk_div_round_rate, |
315 | .set_rate = sh_clk_div_set_rate, |
316 | .enable = sh_clk_div_enable, |
317 | .disable = sh_clk_div_disable, |
318 | .set_parent = sh_clk_div6_set_parent, |
319 | }; |
320 | |
321 | int __init sh_clk_div6_register(struct clk *clks, int nr) |
322 | { |
323 | return sh_clk_div_register_ops(clks, nr, table: &sh_clk_div6_table, |
324 | ops: &sh_clk_div_enable_clk_ops); |
325 | } |
326 | |
327 | int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) |
328 | { |
329 | return sh_clk_div_register_ops(clks, nr, table: &sh_clk_div6_table, |
330 | ops: &sh_clk_div6_reparent_clk_ops); |
331 | } |
332 | |
333 | /* |
334 | * div4 support |
335 | */ |
336 | static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) |
337 | { |
338 | struct clk_div_mult_table *table = clk_to_div_mult_table(clk); |
339 | u32 value; |
340 | int ret; |
341 | |
342 | /* we really need a better way to determine parent index, but for |
343 | * now assume internal parent comes with CLK_ENABLE_ON_INIT set, |
344 | * no CLK_ENABLE_ON_INIT means external clock... |
345 | */ |
346 | |
347 | if (parent->flags & CLK_ENABLE_ON_INIT) |
348 | value = sh_clk_read(clk) & ~(1 << 7); |
349 | else |
350 | value = sh_clk_read(clk) | (1 << 7); |
351 | |
352 | ret = clk_reparent(child: clk, parent); |
353 | if (ret < 0) |
354 | return ret; |
355 | |
356 | sh_clk_write(value, clk); |
357 | |
358 | /* Rebiuld the frequency table */ |
359 | clk_rate_table_build(clk, freq_table: clk->freq_table, nr_freqs: table->nr_divisors, |
360 | src_table: table, bitmap: &clk->arch_flags); |
361 | |
362 | return 0; |
363 | } |
364 | |
365 | static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { |
366 | .recalc = sh_clk_div_recalc, |
367 | .set_rate = sh_clk_div_set_rate, |
368 | .round_rate = sh_clk_div_round_rate, |
369 | .enable = sh_clk_div_enable, |
370 | .disable = sh_clk_div_disable, |
371 | .set_parent = sh_clk_div4_set_parent, |
372 | }; |
373 | |
374 | int __init sh_clk_div4_register(struct clk *clks, int nr, |
375 | struct clk_div4_table *table) |
376 | { |
377 | return sh_clk_div_register_ops(clks, nr, table, ops: &sh_clk_div_clk_ops); |
378 | } |
379 | |
380 | int __init sh_clk_div4_enable_register(struct clk *clks, int nr, |
381 | struct clk_div4_table *table) |
382 | { |
383 | return sh_clk_div_register_ops(clks, nr, table, |
384 | ops: &sh_clk_div_enable_clk_ops); |
385 | } |
386 | |
387 | int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, |
388 | struct clk_div4_table *table) |
389 | { |
390 | return sh_clk_div_register_ops(clks, nr, table, |
391 | ops: &sh_clk_div4_reparent_clk_ops); |
392 | } |
393 | |
394 | /* FSI-DIV */ |
395 | static unsigned long fsidiv_recalc(struct clk *clk) |
396 | { |
397 | u32 value; |
398 | |
399 | value = __raw_readl(addr: clk->mapping->base); |
400 | |
401 | value >>= 16; |
402 | if (value < 2) |
403 | return clk->parent->rate; |
404 | |
405 | return clk->parent->rate / value; |
406 | } |
407 | |
408 | static long fsidiv_round_rate(struct clk *clk, unsigned long rate) |
409 | { |
410 | return clk_rate_div_range_round(clk, div_min: 1, div_max: 0xffff, rate); |
411 | } |
412 | |
413 | static void fsidiv_disable(struct clk *clk) |
414 | { |
415 | __raw_writel(val: 0, addr: clk->mapping->base); |
416 | } |
417 | |
418 | static int fsidiv_enable(struct clk *clk) |
419 | { |
420 | u32 value; |
421 | |
422 | value = __raw_readl(addr: clk->mapping->base) >> 16; |
423 | if (value < 2) |
424 | return 0; |
425 | |
426 | __raw_writel(val: (value << 16) | 0x3, addr: clk->mapping->base); |
427 | |
428 | return 0; |
429 | } |
430 | |
431 | static int fsidiv_set_rate(struct clk *clk, unsigned long rate) |
432 | { |
433 | int idx; |
434 | |
435 | idx = (clk->parent->rate / rate) & 0xffff; |
436 | if (idx < 2) |
437 | __raw_writel(val: 0, addr: clk->mapping->base); |
438 | else |
439 | __raw_writel(val: idx << 16, addr: clk->mapping->base); |
440 | |
441 | return 0; |
442 | } |
443 | |
444 | static struct sh_clk_ops fsidiv_clk_ops = { |
445 | .recalc = fsidiv_recalc, |
446 | .round_rate = fsidiv_round_rate, |
447 | .set_rate = fsidiv_set_rate, |
448 | .enable = fsidiv_enable, |
449 | .disable = fsidiv_disable, |
450 | }; |
451 | |
452 | int __init sh_clk_fsidiv_register(struct clk *clks, int nr) |
453 | { |
454 | struct clk_mapping *map; |
455 | int i; |
456 | |
457 | for (i = 0; i < nr; i++) { |
458 | |
459 | map = kzalloc(size: sizeof(struct clk_mapping), GFP_KERNEL); |
460 | if (!map) { |
461 | pr_err("%s: unable to alloc memory\n" , __func__); |
462 | return -ENOMEM; |
463 | } |
464 | |
465 | /* clks[i].enable_reg came from SH_CLK_FSIDIV() */ |
466 | map->phys = (phys_addr_t)clks[i].enable_reg; |
467 | map->len = 8; |
468 | |
469 | clks[i].enable_reg = 0; /* remove .enable_reg */ |
470 | clks[i].ops = &fsidiv_clk_ops; |
471 | clks[i].mapping = map; |
472 | |
473 | clk_register(&clks[i]); |
474 | } |
475 | |
476 | return 0; |
477 | } |
478 | |