]>
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> |
c0468b02 | 30 | #include <plat/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 | |
b3bf41be BD |
133 | static struct clk_ops clk_pwm_scaler_ops = { |
134 | .get_rate = clk_pwm_scaler_get_rate, | |
135 | .set_rate = clk_pwm_scaler_set_rate, | |
136 | .round_rate = clk_pwm_scaler_round_rate, | |
137 | }; | |
138 | ||
1442e662 | 139 | static struct clk clk_timer_scaler[] = { |
b999f0db BD |
140 | [0] = { |
141 | .name = "pwm-scaler0", | |
142 | .id = -1, | |
b3bf41be | 143 | .ops = &clk_pwm_scaler_ops, |
b999f0db BD |
144 | }, |
145 | [1] = { | |
146 | .name = "pwm-scaler1", | |
147 | .id = -1, | |
b3bf41be | 148 | .ops = &clk_pwm_scaler_ops, |
b999f0db BD |
149 | }, |
150 | }; | |
151 | ||
1442e662 | 152 | static struct clk clk_timer_tclk[] = { |
b999f0db BD |
153 | [0] = { |
154 | .name = "pwm-tclk0", | |
155 | .id = -1, | |
156 | }, | |
157 | [1] = { | |
158 | .name = "pwm-tclk1", | |
159 | .id = -1, | |
160 | }, | |
161 | }; | |
162 | ||
163 | struct pwm_tdiv_clk { | |
164 | struct clk clk; | |
165 | unsigned int divisor; | |
166 | }; | |
167 | ||
168 | static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) | |
169 | { | |
170 | return container_of(clk, struct pwm_tdiv_clk, clk); | |
171 | } | |
172 | ||
b999f0db BD |
173 | static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) |
174 | { | |
175 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
176 | unsigned int divisor; | |
177 | ||
178 | tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); | |
179 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
180 | ||
b09bcdd4 | 181 | if (pwm_cfg_src_is_tclk(tcfg1)) |
b999f0db BD |
182 | divisor = to_tdiv(clk)->divisor; |
183 | else | |
184 | divisor = tcfg_to_divisor(tcfg1); | |
185 | ||
186 | return clk_get_rate(clk->parent) / divisor; | |
187 | } | |
188 | ||
189 | static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, | |
190 | unsigned long rate) | |
191 | { | |
192 | unsigned long parent_rate; | |
193 | unsigned long divisor; | |
194 | ||
195 | parent_rate = clk_get_rate(clk->parent); | |
196 | divisor = parent_rate / rate; | |
197 | ||
b09bcdd4 BD |
198 | if (divisor <= 1 && pwm_tdiv_has_div1()) |
199 | divisor = 1; | |
200 | else if (divisor <= 2) | |
b999f0db BD |
201 | divisor = 2; |
202 | else if (divisor <= 4) | |
203 | divisor = 4; | |
204 | else if (divisor <= 8) | |
205 | divisor = 8; | |
206 | else | |
207 | divisor = 16; | |
208 | ||
209 | return parent_rate / divisor; | |
210 | } | |
211 | ||
212 | static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) | |
213 | { | |
b09bcdd4 | 214 | return pwm_tdiv_div_bits(divclk->divisor); |
b999f0db BD |
215 | } |
216 | ||
217 | static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) | |
218 | { | |
219 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
220 | unsigned long bits = clk_pwm_tdiv_bits(divclk); | |
221 | unsigned long flags; | |
222 | unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); | |
223 | ||
224 | local_irq_save(flags); | |
225 | ||
226 | tcfg1 = __raw_readl(S3C2410_TCFG1); | |
227 | tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); | |
228 | tcfg1 |= bits << shift; | |
229 | __raw_writel(tcfg1, S3C2410_TCFG1); | |
230 | ||
231 | local_irq_restore(flags); | |
232 | } | |
233 | ||
234 | static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) | |
235 | { | |
236 | struct pwm_tdiv_clk *divclk = to_tdiv(clk); | |
237 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
238 | unsigned long parent_rate = clk_get_rate(clk->parent); | |
239 | unsigned long divisor; | |
240 | ||
241 | tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); | |
242 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
243 | ||
244 | rate = clk_round_rate(clk, rate); | |
245 | divisor = parent_rate / rate; | |
246 | ||
247 | if (divisor > 16) | |
248 | return -EINVAL; | |
249 | ||
250 | divclk->divisor = divisor; | |
251 | ||
252 | /* Update the current MUX settings if we are currently | |
253 | * selected as the clock source for this clock. */ | |
254 | ||
b09bcdd4 | 255 | if (!pwm_cfg_src_is_tclk(tcfg1)) |
b999f0db BD |
256 | clk_pwm_tdiv_update(divclk); |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
b3bf41be BD |
261 | static struct clk_ops clk_tdiv_ops = { |
262 | .get_rate = clk_pwm_tdiv_get_rate, | |
263 | .set_rate = clk_pwm_tdiv_set_rate, | |
264 | .round_rate = clk_pwm_tdiv_round_rate, | |
265 | }; | |
266 | ||
1442e662 | 267 | static struct pwm_tdiv_clk clk_timer_tdiv[] = { |
b999f0db BD |
268 | [0] = { |
269 | .clk = { | |
b3bf41be | 270 | .name = "pwm-tdiv", |
e83626f2 | 271 | .devname = "s3c24xx-pwm.0", |
b3bf41be BD |
272 | .ops = &clk_tdiv_ops, |
273 | .parent = &clk_timer_scaler[0], | |
b999f0db BD |
274 | }, |
275 | }, | |
276 | [1] = { | |
277 | .clk = { | |
b3bf41be | 278 | .name = "pwm-tdiv", |
e83626f2 | 279 | .devname = "s3c24xx-pwm.1", |
b3bf41be BD |
280 | .ops = &clk_tdiv_ops, |
281 | .parent = &clk_timer_scaler[0], | |
b999f0db BD |
282 | } |
283 | }, | |
284 | [2] = { | |
285 | .clk = { | |
b3bf41be | 286 | .name = "pwm-tdiv", |
e83626f2 | 287 | .devname = "s3c24xx-pwm.2", |
b3bf41be BD |
288 | .ops = &clk_tdiv_ops, |
289 | .parent = &clk_timer_scaler[1], | |
b999f0db BD |
290 | }, |
291 | }, | |
292 | [3] = { | |
293 | .clk = { | |
b3bf41be | 294 | .name = "pwm-tdiv", |
e83626f2 | 295 | .devname = "s3c24xx-pwm.3", |
b3bf41be BD |
296 | .ops = &clk_tdiv_ops, |
297 | .parent = &clk_timer_scaler[1], | |
b999f0db BD |
298 | }, |
299 | }, | |
300 | [4] = { | |
301 | .clk = { | |
b3bf41be | 302 | .name = "pwm-tdiv", |
e83626f2 | 303 | .devname = "s3c24xx-pwm.4", |
b3bf41be BD |
304 | .ops = &clk_tdiv_ops, |
305 | .parent = &clk_timer_scaler[1], | |
b999f0db BD |
306 | }, |
307 | }, | |
308 | }; | |
309 | ||
310 | static int __init clk_pwm_tdiv_register(unsigned int id) | |
311 | { | |
312 | struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; | |
313 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
314 | ||
315 | tcfg1 >>= S3C2410_TCFG1_SHIFT(id); | |
316 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
317 | ||
318 | divclk->clk.id = id; | |
319 | divclk->divisor = tcfg_to_divisor(tcfg1); | |
320 | ||
321 | return s3c24xx_register_clock(&divclk->clk); | |
322 | } | |
323 | ||
324 | static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) | |
325 | { | |
326 | return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; | |
327 | } | |
328 | ||
329 | static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) | |
330 | { | |
331 | return &clk_timer_tdiv[id].clk; | |
332 | } | |
333 | ||
334 | static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) | |
335 | { | |
336 | unsigned int id = clk->id; | |
337 | unsigned long tcfg1; | |
338 | unsigned long flags; | |
339 | unsigned long bits; | |
340 | unsigned long shift = S3C2410_TCFG1_SHIFT(id); | |
341 | ||
c0468b02 KK |
342 | unsigned long mux_tclk; |
343 | ||
344 | if (soc_is_s3c24xx()) | |
345 | mux_tclk = S3C2410_TCFG1_MUX_TCLK; | |
346 | else if (soc_is_s5p6440() || soc_is_s5p6450()) | |
347 | mux_tclk = 0; | |
348 | else | |
349 | mux_tclk = S3C64XX_TCFG1_MUX_TCLK; | |
350 | ||
b999f0db | 351 | if (parent == s3c24xx_pwmclk_tclk(id)) |
c0468b02 | 352 | bits = mux_tclk << shift; |
b999f0db | 353 | else if (parent == s3c24xx_pwmclk_tdiv(id)) |
7e90d760 | 354 | bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; |
b999f0db BD |
355 | else |
356 | return -EINVAL; | |
357 | ||
358 | clk->parent = parent; | |
359 | ||
360 | local_irq_save(flags); | |
361 | ||
362 | tcfg1 = __raw_readl(S3C2410_TCFG1); | |
363 | tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); | |
364 | __raw_writel(tcfg1 | bits, S3C2410_TCFG1); | |
365 | ||
366 | local_irq_restore(flags); | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
b3bf41be BD |
371 | static struct clk_ops clk_tin_ops = { |
372 | .set_parent = clk_pwm_tin_set_parent, | |
373 | }; | |
374 | ||
b999f0db BD |
375 | static struct clk clk_tin[] = { |
376 | [0] = { | |
b3bf41be | 377 | .name = "pwm-tin", |
e83626f2 | 378 | .devname = "s3c24xx-pwm.0", |
b3bf41be BD |
379 | .id = 0, |
380 | .ops = &clk_tin_ops, | |
b999f0db BD |
381 | }, |
382 | [1] = { | |
b3bf41be | 383 | .name = "pwm-tin", |
e83626f2 | 384 | .devname = "s3c24xx-pwm.1", |
b3bf41be BD |
385 | .id = 1, |
386 | .ops = &clk_tin_ops, | |
b999f0db BD |
387 | }, |
388 | [2] = { | |
b3bf41be | 389 | .name = "pwm-tin", |
e83626f2 | 390 | .devname = "s3c24xx-pwm.2", |
b3bf41be BD |
391 | .id = 2, |
392 | .ops = &clk_tin_ops, | |
b999f0db BD |
393 | }, |
394 | [3] = { | |
b3bf41be | 395 | .name = "pwm-tin", |
e83626f2 | 396 | .devname = "s3c24xx-pwm.3", |
b3bf41be BD |
397 | .id = 3, |
398 | .ops = &clk_tin_ops, | |
b999f0db BD |
399 | }, |
400 | [4] = { | |
b3bf41be | 401 | .name = "pwm-tin", |
e83626f2 | 402 | .devname = "s3c24xx-pwm.4", |
b3bf41be BD |
403 | .id = 4, |
404 | .ops = &clk_tin_ops, | |
b999f0db BD |
405 | }, |
406 | }; | |
407 | ||
408 | static __init int clk_pwm_tin_register(struct clk *pwm) | |
409 | { | |
410 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
411 | unsigned int id = pwm->id; | |
412 | ||
413 | struct clk *parent; | |
414 | int ret; | |
415 | ||
416 | ret = s3c24xx_register_clock(pwm); | |
417 | if (ret < 0) | |
418 | return ret; | |
419 | ||
420 | tcfg1 >>= S3C2410_TCFG1_SHIFT(id); | |
421 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
422 | ||
b09bcdd4 | 423 | if (pwm_cfg_src_is_tclk(tcfg1)) |
b999f0db BD |
424 | parent = s3c24xx_pwmclk_tclk(id); |
425 | else | |
426 | parent = s3c24xx_pwmclk_tdiv(id); | |
427 | ||
428 | return clk_set_parent(pwm, parent); | |
429 | } | |
430 | ||
9d325f23 BD |
431 | /** |
432 | * s3c_pwmclk_init() - initialise pwm clocks | |
433 | * | |
434 | * Initialise and register the clocks which provide the inputs for the | |
435 | * pwm timer blocks. | |
436 | * | |
437 | * Note, this call is required by the time core, so must be called after | |
438 | * the base clocks are added and before any of the initcalls are run. | |
439 | */ | |
440 | __init void s3c_pwmclk_init(void) | |
b999f0db BD |
441 | { |
442 | struct clk *clk_timers; | |
443 | unsigned int clk; | |
444 | int ret; | |
445 | ||
446 | clk_timers = clk_get(NULL, "timers"); | |
447 | if (IS_ERR(clk_timers)) { | |
448 | printk(KERN_ERR "%s: no parent clock\n", __func__); | |
9d325f23 | 449 | return; |
b999f0db BD |
450 | } |
451 | ||
1d9f13c4 | 452 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) |
b999f0db | 453 | clk_timer_scaler[clk].parent = clk_timers; |
b999f0db | 454 | |
1d9f13c4 BD |
455 | s3c_register_clocks(clk_timer_scaler, ARRAY_SIZE(clk_timer_scaler)); |
456 | s3c_register_clocks(clk_timer_tclk, ARRAY_SIZE(clk_timer_tclk)); | |
b999f0db BD |
457 | |
458 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { | |
459 | ret = clk_pwm_tdiv_register(clk); | |
1d9f13c4 | 460 | |
b999f0db BD |
461 | if (ret < 0) { |
462 | printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); | |
9d325f23 | 463 | return; |
b999f0db BD |
464 | } |
465 | } | |
466 | ||
467 | for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { | |
468 | ret = clk_pwm_tin_register(&clk_tin[clk]); | |
469 | if (ret < 0) { | |
470 | printk(KERN_ERR "error adding pwm%d tin clock\n", clk); | |
9d325f23 | 471 | return; |
b999f0db BD |
472 | } |
473 | } | |
b999f0db | 474 | } |