Commit | Line | Data |
---|---|---|
b999f0db BD |
1 | /* linux/arch/arm/plat-s3c24xx/pwm-clock.c |
2 | * | |
3 | * Copyright (c) 2007 Simtec Electronics | |
4 | * Copyright (c) 2007, 2008 Ben Dooks | |
5 | * Ben Dooks <ben-linux@fluff.org> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License. | |
10 | */ | |
11 | ||
12 | #include <linux/init.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/list.h> | |
16 | #include <linux/errno.h> | |
b09bcdd4 | 17 | #include <linux/log2.h> |
b999f0db BD |
18 | #include <linux/clk.h> |
19 | #include <linux/err.h> | |
20 | #include <linux/io.h> | |
21 | ||
a09e64fb | 22 | #include <mach/hardware.h> |
e550ae74 | 23 | #include <mach/map.h> |
b999f0db BD |
24 | #include <asm/irq.h> |
25 | ||
d5120ae7 | 26 | #include <plat/clock.h> |
a2b7ba9c | 27 | #include <plat/cpu.h> |
b999f0db | 28 | |
a2b7ba9c | 29 | #include <plat/regs-timer.h> |
b09bcdd4 | 30 | #include <mach/pwm-clock.h> |
b999f0db BD |
31 | |
32 | /* Each of the timers 0 through 5 go through the following | |
33 | * clock tree, with the inputs depending on the timers. | |
34 | * | |
35 | * pclk ---- [ prescaler 0 ] -+---> timer 0 | |
36 | * +---> timer 1 | |
37 | * | |
38 | * pclk ---- [ prescaler 1 ] -+---> timer 2 | |
39 | * +---> timer 3 | |
40 | * \---> timer 4 | |
41 | * | |
42 | * Which are fed into the timers as so: | |
43 | * | |
44 | * prescaled 0 ---- [ div 2,4,8,16 ] ---\ | |
45 | * [mux] -> timer 0 | |
46 | * tclk 0 ------------------------------/ | |
47 | * | |
48 | * prescaled 0 ---- [ div 2,4,8,16 ] ---\ | |
49 | * [mux] -> timer 1 | |
50 | * tclk 0 ------------------------------/ | |
51 | * | |
52 | * | |
53 | * prescaled 1 ---- [ div 2,4,8,16 ] ---\ | |
54 | * [mux] -> timer 2 | |
55 | * tclk 1 ------------------------------/ | |
56 | * | |
57 | * prescaled 1 ---- [ div 2,4,8,16 ] ---\ | |
58 | * [mux] -> timer 3 | |
59 | * tclk 1 ------------------------------/ | |
60 | * | |
61 | * prescaled 1 ---- [ div 2,4,8, 16 ] --\ | |
62 | * [mux] -> timer 4 | |
63 | * tclk 1 ------------------------------/ | |
64 | * | |
65 | * Since the mux and the divider are tied together in the | |
66 | * same register space, it is impossible to set the parent | |
67 | * and the rate at the same time. To avoid this, we add an | |
68 | * intermediate 'prescaled-and-divided' clock to select | |
69 | * as the parent for the timer input clock called tdiv. | |
70 | * | |
71 | * prescaled clk --> pwm-tdiv ---\ | |
72 | * [ mux ] --> timer X | |
73 | * tclk -------------------------/ | |
74 | */ | |
75 | ||
7d2dbcf9 BD |
76 | static struct clk clk_timer_scaler[]; |
77 | ||
82fd8e68 | 78 | static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) |
b999f0db BD |
79 | { |
80 | unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); | |
81 | ||
7d2dbcf9 | 82 | if (clk == &clk_timer_scaler[1]) { |
b999f0db BD |
83 | tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; |
84 | tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; | |
85 | } else { | |
86 | tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; | |
87 | } | |
88 | ||
89 | return clk_get_rate(clk->parent) / (tcfg0 + 1); | |
90 | } | |
91 | ||
82fd8e68 BD |
92 | static unsigned long clk_pwm_scaler_round_rate(struct clk *clk, |
93 | unsigned long rate) | |
94 | { | |
95 | unsigned long parent_rate = clk_get_rate(clk->parent); | |
96 | unsigned long divisor = parent_rate / rate; | |
97 | ||
98 | if (divisor > 256) | |
99 | divisor = 256; | |
100 | else if (divisor < 2) | |
101 | divisor = 2; | |
102 | ||
103 | return parent_rate / divisor; | |
104 | } | |
105 | ||
106 | static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) | |
107 | { | |
108 | unsigned long round = clk_pwm_scaler_round_rate(clk, rate); | |
109 | unsigned long tcfg0; | |
110 | unsigned long divisor; | |
111 | unsigned long flags; | |
112 | ||
113 | divisor = clk_get_rate(clk->parent) / round; | |
114 | divisor--; | |
115 | ||
116 | local_irq_save(flags); | |
117 | tcfg0 = __raw_readl(S3C2410_TCFG0); | |
118 | ||
7d2dbcf9 | 119 | if (clk == &clk_timer_scaler[1]) { |
82fd8e68 BD |
120 | tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; |
121 | tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; | |
122 | } else { | |
123 | tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; | |
124 | tcfg0 |= divisor; | |
125 | } | |
126 | ||
127 | __raw_writel(tcfg0, S3C2410_TCFG0); | |
128 | local_irq_restore(flags); | |
129 | ||
130 | return 0; | |
131 | } | |
b999f0db | 132 | |
1442e662 | 133 | static struct clk clk_timer_scaler[] = { |
b999f0db BD |
134 | [0] = { |
135 | .name = "pwm-scaler0", | |
136 | .id = -1, | |
82fd8e68 BD |
137 | .get_rate = clk_pwm_scaler_get_rate, |
138 | .set_rate = clk_pwm_scaler_set_rate, | |
139 | .round_rate = clk_pwm_scaler_round_rate, | |
b999f0db BD |
140 | }, |
141 | [1] = { | |
142 | .name = "pwm-scaler1", | |
143 | .id = -1, | |
82fd8e68 BD |
144 | .get_rate = clk_pwm_scaler_get_rate, |
145 | .set_rate = clk_pwm_scaler_set_rate, | |
146 | .round_rate = clk_pwm_scaler_round_rate, | |
b999f0db BD |
147 | }, |
148 | }; | |
149 | ||
1442e662 | 150 | static struct clk clk_timer_tclk[] = { |
b999f0db BD |
151 | [0] = { |
152 | .name = "pwm-tclk0", | |
153 | .id = -1, | |
154 | }, | |
155 | [1] = { | |
156 | .name = "pwm-tclk1", | |
157 | .id = -1, | |
158 | }, | |
159 | }; | |
160 | ||
161 | struct pwm_tdiv_clk { | |
162 | struct clk clk; | |
163 | unsigned int divisor; | |
164 | }; | |
165 | ||
166 | static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) | |
167 | { | |
168 | return container_of(clk, struct pwm_tdiv_clk, clk); | |
169 | } | |
170 | ||
b999f0db BD |
171 | static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) |
172 | { | |
173 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
174 | unsigned int divisor; | |
175 | ||
176 | tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); | |
177 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
178 | ||
b09bcdd4 | 179 | if (pwm_cfg_src_is_tclk(tcfg1)) |
b999f0db BD |
180 | divisor = to_tdiv(clk)->divisor; |
181 | else | |
182 | divisor = tcfg_to_divisor(tcfg1); | |
183 | ||
184 | return clk_get_rate(clk->parent) / divisor; | |
185 | } | |
186 | ||
187 | static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, | |
188 | unsigned long rate) | |
189 | { | |
190 | unsigned long parent_rate; | |
191 | unsigned long divisor; | |
192 | ||
193 | parent_rate = clk_get_rate(clk->parent); | |
194 | divisor = parent_rate / rate; | |
195 | ||
b09bcdd4 BD |
196 | if (divisor <= 1 && pwm_tdiv_has_div1()) |
197 | divisor = 1; | |
198 | else if (divisor <= 2) | |
b999f0db BD |
199 | divisor = 2; |
200 | else if (divisor <= 4) | |
201 | divisor = 4; | |
202 | else if (divisor <= 8) | |
203 | divisor = 8; | |
204 | else | |
205 | divisor = 16; | |
206 | ||
207 | return parent_rate / divisor; | |
208 | } | |
209 | ||
210 | static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) | |
211 | { | |
b09bcdd4 | 212 | return pwm_tdiv_div_bits(divclk->divisor); |
b999f0db BD |
213 | } |
214 | ||
215 | static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) | |
216 | { | |
217 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
218 | unsigned long bits = clk_pwm_tdiv_bits(divclk); | |
219 | unsigned long flags; | |
220 | unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); | |
221 | ||
222 | local_irq_save(flags); | |
223 | ||
224 | tcfg1 = __raw_readl(S3C2410_TCFG1); | |
225 | tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); | |
226 | tcfg1 |= bits << shift; | |
227 | __raw_writel(tcfg1, S3C2410_TCFG1); | |
228 | ||
229 | local_irq_restore(flags); | |
230 | } | |
231 | ||
232 | static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) | |
233 | { | |
234 | struct pwm_tdiv_clk *divclk = to_tdiv(clk); | |
235 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
236 | unsigned long parent_rate = clk_get_rate(clk->parent); | |
237 | unsigned long divisor; | |
238 | ||
239 | tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); | |
240 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
241 | ||
242 | rate = clk_round_rate(clk, rate); | |
243 | divisor = parent_rate / rate; | |
244 | ||
245 | if (divisor > 16) | |
246 | return -EINVAL; | |
247 | ||
248 | divclk->divisor = divisor; | |
249 | ||
250 | /* Update the current MUX settings if we are currently | |
251 | * selected as the clock source for this clock. */ | |
252 | ||
b09bcdd4 | 253 | if (!pwm_cfg_src_is_tclk(tcfg1)) |
b999f0db BD |
254 | clk_pwm_tdiv_update(divclk); |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
1442e662 | 259 | static struct pwm_tdiv_clk clk_timer_tdiv[] = { |
b999f0db BD |
260 | [0] = { |
261 | .clk = { | |
262 | .name = "pwm-tdiv", | |
263 | .parent = &clk_timer_scaler[0], | |
264 | .get_rate = clk_pwm_tdiv_get_rate, | |
265 | .set_rate = clk_pwm_tdiv_set_rate, | |
266 | .round_rate = clk_pwm_tdiv_round_rate, | |
267 | }, | |
268 | }, | |
269 | [1] = { | |
270 | .clk = { | |
271 | .name = "pwm-tdiv", | |
272 | .parent = &clk_timer_scaler[0], | |
273 | .get_rate = clk_pwm_tdiv_get_rate, | |
274 | .set_rate = clk_pwm_tdiv_set_rate, | |
275 | .round_rate = clk_pwm_tdiv_round_rate, | |
276 | } | |
277 | }, | |
278 | [2] = { | |
279 | .clk = { | |
280 | .name = "pwm-tdiv", | |
281 | .parent = &clk_timer_scaler[1], | |
282 | .get_rate = clk_pwm_tdiv_get_rate, | |
283 | .set_rate = clk_pwm_tdiv_set_rate, | |
284 | .round_rate = clk_pwm_tdiv_round_rate, | |
285 | }, | |
286 | }, | |
287 | [3] = { | |
288 | .clk = { | |
289 | .name = "pwm-tdiv", | |
290 | .parent = &clk_timer_scaler[1], | |
291 | .get_rate = clk_pwm_tdiv_get_rate, | |
292 | .set_rate = clk_pwm_tdiv_set_rate, | |
293 | .round_rate = clk_pwm_tdiv_round_rate, | |
294 | }, | |
295 | }, | |
296 | [4] = { | |
297 | .clk = { | |
298 | .name = "pwm-tdiv", | |
299 | .parent = &clk_timer_scaler[1], | |
300 | .get_rate = clk_pwm_tdiv_get_rate, | |
301 | .set_rate = clk_pwm_tdiv_set_rate, | |
302 | .round_rate = clk_pwm_tdiv_round_rate, | |
303 | }, | |
304 | }, | |
305 | }; | |
306 | ||
307 | static int __init clk_pwm_tdiv_register(unsigned int id) | |
308 | { | |
309 | struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; | |
310 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
311 | ||
312 | tcfg1 >>= S3C2410_TCFG1_SHIFT(id); | |
313 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
314 | ||
315 | divclk->clk.id = id; | |
316 | divclk->divisor = tcfg_to_divisor(tcfg1); | |
317 | ||
318 | return s3c24xx_register_clock(&divclk->clk); | |
319 | } | |
320 | ||
321 | static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) | |
322 | { | |
323 | return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; | |
324 | } | |
325 | ||
326 | static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) | |
327 | { | |
328 | return &clk_timer_tdiv[id].clk; | |
329 | } | |
330 | ||
331 | static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) | |
332 | { | |
333 | unsigned int id = clk->id; | |
334 | unsigned long tcfg1; | |
335 | unsigned long flags; | |
336 | unsigned long bits; | |
337 | unsigned long shift = S3C2410_TCFG1_SHIFT(id); | |
338 | ||
339 | if (parent == s3c24xx_pwmclk_tclk(id)) | |
b09bcdd4 | 340 | bits = S3C_TCFG1_MUX_TCLK << shift; |
b999f0db | 341 | else if (parent == s3c24xx_pwmclk_tdiv(id)) |
7e90d760 | 342 | bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; |
b999f0db BD |
343 | else |
344 | return -EINVAL; | |
345 | ||
346 | clk->parent = parent; | |
347 | ||
348 | local_irq_save(flags); | |
349 | ||
350 | tcfg1 = __raw_readl(S3C2410_TCFG1); | |
351 | tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); | |
352 | __raw_writel(tcfg1 | bits, S3C2410_TCFG1); | |
353 | ||
354 | local_irq_restore(flags); | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
359 | static struct clk clk_tin[] = { | |
360 | [0] = { | |
361 | .name = "pwm-tin", | |
362 | .id = 0, | |
363 | .set_parent = clk_pwm_tin_set_parent, | |
364 | }, | |
365 | [1] = { | |
366 | .name = "pwm-tin", | |
367 | .id = 1, | |
368 | .set_parent = clk_pwm_tin_set_parent, | |
369 | }, | |
370 | [2] = { | |
371 | .name = "pwm-tin", | |
372 | .id = 2, | |
373 | .set_parent = clk_pwm_tin_set_parent, | |
374 | }, | |
375 | [3] = { | |
376 | .name = "pwm-tin", | |
377 | .id = 3, | |
378 | .set_parent = clk_pwm_tin_set_parent, | |
379 | }, | |
380 | [4] = { | |
381 | .name = "pwm-tin", | |
382 | .id = 4, | |
383 | .set_parent = clk_pwm_tin_set_parent, | |
384 | }, | |
385 | }; | |
386 | ||
387 | static __init int clk_pwm_tin_register(struct clk *pwm) | |
388 | { | |
389 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
390 | unsigned int id = pwm->id; | |
391 | ||
392 | struct clk *parent; | |
393 | int ret; | |
394 | ||
395 | ret = s3c24xx_register_clock(pwm); | |
396 | if (ret < 0) | |
397 | return ret; | |
398 | ||
399 | tcfg1 >>= S3C2410_TCFG1_SHIFT(id); | |
400 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
401 | ||
b09bcdd4 | 402 | if (pwm_cfg_src_is_tclk(tcfg1)) |
b999f0db BD |
403 | parent = s3c24xx_pwmclk_tclk(id); |
404 | else | |
405 | parent = s3c24xx_pwmclk_tdiv(id); | |
406 | ||
407 | return clk_set_parent(pwm, parent); | |
408 | } | |
409 | ||
9d325f23 BD |
410 | /** |
411 | * s3c_pwmclk_init() - initialise pwm clocks | |
412 | * | |
413 | * Initialise and register the clocks which provide the inputs for the | |
414 | * pwm timer blocks. | |
415 | * | |
416 | * Note, this call is required by the time core, so must be called after | |
417 | * the base clocks are added and before any of the initcalls are run. | |
418 | */ | |
419 | __init void s3c_pwmclk_init(void) | |
b999f0db BD |
420 | { |
421 | struct clk *clk_timers; | |
422 | unsigned int clk; | |
423 | int ret; | |
424 | ||
425 | clk_timers = clk_get(NULL, "timers"); | |
426 | if (IS_ERR(clk_timers)) { | |
427 | printk(KERN_ERR "%s: no parent clock\n", __func__); | |
9d325f23 | 428 | return; |
b999f0db BD |
429 | } |
430 | ||
431 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { | |
432 | clk_timer_scaler[clk].parent = clk_timers; | |
433 | ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); | |
434 | if (ret < 0) { | |
435 | printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); | |
9d325f23 | 436 | return; |
b999f0db BD |
437 | } |
438 | } | |
439 | ||
440 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { | |
441 | ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); | |
442 | if (ret < 0) { | |
443 | printk(KERN_ERR "error adding pww tclk%d\n", clk); | |
9d325f23 | 444 | return; |
b999f0db BD |
445 | } |
446 | } | |
447 | ||
448 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { | |
449 | ret = clk_pwm_tdiv_register(clk); | |
450 | if (ret < 0) { | |
451 | printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); | |
9d325f23 | 452 | return; |
b999f0db BD |
453 | } |
454 | } | |
455 | ||
456 | for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { | |
457 | ret = clk_pwm_tin_register(&clk_tin[clk]); | |
458 | if (ret < 0) { | |
459 | printk(KERN_ERR "error adding pwm%d tin clock\n", clk); | |
9d325f23 | 460 | return; |
b999f0db BD |
461 | } |
462 | } | |
b999f0db | 463 | } |