blob: 5242fb0afcca0f8a52aaac26c51e40f532e7b28b [file] [log] [blame]
Ben Dooksb999f0d2008-07-03 11:24:27 +01001/* 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>
17#include <linux/clk.h>
18#include <linux/err.h>
19#include <linux/io.h>
20
Russell Kinga09e64f2008-08-05 16:14:15 +010021#include <mach/hardware.h>
Ben Dookse550ae72008-10-21 14:06:56 +010022#include <mach/map.h>
Ben Dooksb999f0d2008-07-03 11:24:27 +010023#include <asm/irq.h>
24
Ben Dooksd5120ae2008-10-07 23:09:51 +010025#include <plat/clock.h>
Ben Dooksa2b7ba92008-10-07 22:26:09 +010026#include <plat/cpu.h>
Ben Dooksb999f0d2008-07-03 11:24:27 +010027
Ben Dooksa2b7ba92008-10-07 22:26:09 +010028#include <plat/regs-timer.h>
Ben Dooksb999f0d2008-07-03 11:24:27 +010029
30/* Each of the timers 0 through 5 go through the following
31 * clock tree, with the inputs depending on the timers.
32 *
33 * pclk ---- [ prescaler 0 ] -+---> timer 0
34 * +---> timer 1
35 *
36 * pclk ---- [ prescaler 1 ] -+---> timer 2
37 * +---> timer 3
38 * \---> timer 4
39 *
40 * Which are fed into the timers as so:
41 *
42 * prescaled 0 ---- [ div 2,4,8,16 ] ---\
43 * [mux] -> timer 0
44 * tclk 0 ------------------------------/
45 *
46 * prescaled 0 ---- [ div 2,4,8,16 ] ---\
47 * [mux] -> timer 1
48 * tclk 0 ------------------------------/
49 *
50 *
51 * prescaled 1 ---- [ div 2,4,8,16 ] ---\
52 * [mux] -> timer 2
53 * tclk 1 ------------------------------/
54 *
55 * prescaled 1 ---- [ div 2,4,8,16 ] ---\
56 * [mux] -> timer 3
57 * tclk 1 ------------------------------/
58 *
59 * prescaled 1 ---- [ div 2,4,8, 16 ] --\
60 * [mux] -> timer 4
61 * tclk 1 ------------------------------/
62 *
63 * Since the mux and the divider are tied together in the
64 * same register space, it is impossible to set the parent
65 * and the rate at the same time. To avoid this, we add an
66 * intermediate 'prescaled-and-divided' clock to select
67 * as the parent for the timer input clock called tdiv.
68 *
69 * prescaled clk --> pwm-tdiv ---\
70 * [ mux ] --> timer X
71 * tclk -------------------------/
72*/
73
Ben Dooks7d2dbcf2008-11-21 10:36:06 +000074static struct clk clk_timer_scaler[];
75
Ben Dooks82fd8e62008-11-21 10:36:04 +000076static unsigned long clk_pwm_scaler_get_rate(struct clk *clk)
Ben Dooksb999f0d2008-07-03 11:24:27 +010077{
78 unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
79
Ben Dooks7d2dbcf2008-11-21 10:36:06 +000080 if (clk == &clk_timer_scaler[1]) {
Ben Dooksb999f0d2008-07-03 11:24:27 +010081 tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
82 tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
83 } else {
84 tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK;
85 }
86
87 return clk_get_rate(clk->parent) / (tcfg0 + 1);
88}
89
Ben Dooks82fd8e62008-11-21 10:36:04 +000090static unsigned long clk_pwm_scaler_round_rate(struct clk *clk,
91 unsigned long rate)
92{
93 unsigned long parent_rate = clk_get_rate(clk->parent);
94 unsigned long divisor = parent_rate / rate;
95
96 if (divisor > 256)
97 divisor = 256;
98 else if (divisor < 2)
99 divisor = 2;
100
101 return parent_rate / divisor;
102}
103
104static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate)
105{
106 unsigned long round = clk_pwm_scaler_round_rate(clk, rate);
107 unsigned long tcfg0;
108 unsigned long divisor;
109 unsigned long flags;
110
111 divisor = clk_get_rate(clk->parent) / round;
112 divisor--;
113
114 local_irq_save(flags);
115 tcfg0 = __raw_readl(S3C2410_TCFG0);
116
Ben Dooks7d2dbcf2008-11-21 10:36:06 +0000117 if (clk == &clk_timer_scaler[1]) {
Ben Dooks82fd8e62008-11-21 10:36:04 +0000118 tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
119 tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT;
120 } else {
121 tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
122 tcfg0 |= divisor;
123 }
124
125 __raw_writel(tcfg0, S3C2410_TCFG0);
126 local_irq_restore(flags);
127
128 return 0;
129}
Ben Dooksb999f0d2008-07-03 11:24:27 +0100130
Ben Dooks1442e662008-08-26 22:54:04 +0100131static struct clk clk_timer_scaler[] = {
Ben Dooksb999f0d2008-07-03 11:24:27 +0100132 [0] = {
133 .name = "pwm-scaler0",
134 .id = -1,
Ben Dooks82fd8e62008-11-21 10:36:04 +0000135 .get_rate = clk_pwm_scaler_get_rate,
136 .set_rate = clk_pwm_scaler_set_rate,
137 .round_rate = clk_pwm_scaler_round_rate,
Ben Dooksb999f0d2008-07-03 11:24:27 +0100138 },
139 [1] = {
140 .name = "pwm-scaler1",
141 .id = -1,
Ben Dooks82fd8e62008-11-21 10:36:04 +0000142 .get_rate = clk_pwm_scaler_get_rate,
143 .set_rate = clk_pwm_scaler_set_rate,
144 .round_rate = clk_pwm_scaler_round_rate,
Ben Dooksb999f0d2008-07-03 11:24:27 +0100145 },
146};
147
Ben Dooks1442e662008-08-26 22:54:04 +0100148static struct clk clk_timer_tclk[] = {
Ben Dooksb999f0d2008-07-03 11:24:27 +0100149 [0] = {
150 .name = "pwm-tclk0",
151 .id = -1,
152 },
153 [1] = {
154 .name = "pwm-tclk1",
155 .id = -1,
156 },
157};
158
159struct pwm_tdiv_clk {
160 struct clk clk;
161 unsigned int divisor;
162};
163
164static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
165{
166 return container_of(clk, struct pwm_tdiv_clk, clk);
167}
168
169static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
170{
171 return 1 << (1 + tcfg1);
172}
173
174static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
175{
176 unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
177 unsigned int divisor;
178
179 tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
180 tcfg1 &= S3C2410_TCFG1_MUX_MASK;
181
182 if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
183 divisor = to_tdiv(clk)->divisor;
184 else
185 divisor = tcfg_to_divisor(tcfg1);
186
187 return clk_get_rate(clk->parent) / divisor;
188}
189
190static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
191 unsigned long rate)
192{
193 unsigned long parent_rate;
194 unsigned long divisor;
195
196 parent_rate = clk_get_rate(clk->parent);
197 divisor = parent_rate / rate;
198
199 if (divisor <= 2)
200 divisor = 2;
201 else if (divisor <= 4)
202 divisor = 4;
203 else if (divisor <= 8)
204 divisor = 8;
205 else
206 divisor = 16;
207
208 return parent_rate / divisor;
209}
210
211static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
212{
213 unsigned long bits;
214
215 switch (divclk->divisor) {
216 case 2:
217 bits = S3C2410_TCFG1_MUX_DIV2;
218 break;
219 case 4:
220 bits = S3C2410_TCFG1_MUX_DIV4;
221 break;
222 case 8:
223 bits = S3C2410_TCFG1_MUX_DIV8;
224 break;
225 case 16:
226 default:
227 bits = S3C2410_TCFG1_MUX_DIV16;
228 break;
229 }
230
231 return bits;
232}
233
234static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
235{
236 unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
237 unsigned long bits = clk_pwm_tdiv_bits(divclk);
238 unsigned long flags;
239 unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id);
240
241 local_irq_save(flags);
242
243 tcfg1 = __raw_readl(S3C2410_TCFG1);
244 tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
245 tcfg1 |= bits << shift;
246 __raw_writel(tcfg1, S3C2410_TCFG1);
247
248 local_irq_restore(flags);
249}
250
251static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
252{
253 struct pwm_tdiv_clk *divclk = to_tdiv(clk);
254 unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
255 unsigned long parent_rate = clk_get_rate(clk->parent);
256 unsigned long divisor;
257
258 tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
259 tcfg1 &= S3C2410_TCFG1_MUX_MASK;
260
261 rate = clk_round_rate(clk, rate);
262 divisor = parent_rate / rate;
263
264 if (divisor > 16)
265 return -EINVAL;
266
267 divclk->divisor = divisor;
268
269 /* Update the current MUX settings if we are currently
270 * selected as the clock source for this clock. */
271
272 if (tcfg1 != S3C2410_TCFG1_MUX_TCLK)
273 clk_pwm_tdiv_update(divclk);
274
275 return 0;
276}
277
Ben Dooks1442e662008-08-26 22:54:04 +0100278static struct pwm_tdiv_clk clk_timer_tdiv[] = {
Ben Dooksb999f0d2008-07-03 11:24:27 +0100279 [0] = {
280 .clk = {
281 .name = "pwm-tdiv",
282 .parent = &clk_timer_scaler[0],
283 .get_rate = clk_pwm_tdiv_get_rate,
284 .set_rate = clk_pwm_tdiv_set_rate,
285 .round_rate = clk_pwm_tdiv_round_rate,
286 },
287 },
288 [1] = {
289 .clk = {
290 .name = "pwm-tdiv",
291 .parent = &clk_timer_scaler[0],
292 .get_rate = clk_pwm_tdiv_get_rate,
293 .set_rate = clk_pwm_tdiv_set_rate,
294 .round_rate = clk_pwm_tdiv_round_rate,
295 }
296 },
297 [2] = {
298 .clk = {
299 .name = "pwm-tdiv",
300 .parent = &clk_timer_scaler[1],
301 .get_rate = clk_pwm_tdiv_get_rate,
302 .set_rate = clk_pwm_tdiv_set_rate,
303 .round_rate = clk_pwm_tdiv_round_rate,
304 },
305 },
306 [3] = {
307 .clk = {
308 .name = "pwm-tdiv",
309 .parent = &clk_timer_scaler[1],
310 .get_rate = clk_pwm_tdiv_get_rate,
311 .set_rate = clk_pwm_tdiv_set_rate,
312 .round_rate = clk_pwm_tdiv_round_rate,
313 },
314 },
315 [4] = {
316 .clk = {
317 .name = "pwm-tdiv",
318 .parent = &clk_timer_scaler[1],
319 .get_rate = clk_pwm_tdiv_get_rate,
320 .set_rate = clk_pwm_tdiv_set_rate,
321 .round_rate = clk_pwm_tdiv_round_rate,
322 },
323 },
324};
325
326static int __init clk_pwm_tdiv_register(unsigned int id)
327{
328 struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
329 unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
330
331 tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
332 tcfg1 &= S3C2410_TCFG1_MUX_MASK;
333
334 divclk->clk.id = id;
335 divclk->divisor = tcfg_to_divisor(tcfg1);
336
337 return s3c24xx_register_clock(&divclk->clk);
338}
339
340static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
341{
342 return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
343}
344
345static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
346{
347 return &clk_timer_tdiv[id].clk;
348}
349
350static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
351{
352 unsigned int id = clk->id;
353 unsigned long tcfg1;
354 unsigned long flags;
355 unsigned long bits;
356 unsigned long shift = S3C2410_TCFG1_SHIFT(id);
357
358 if (parent == s3c24xx_pwmclk_tclk(id))
359 bits = S3C2410_TCFG1_MUX_TCLK << shift;
360 else if (parent == s3c24xx_pwmclk_tdiv(id))
Dallas Foley7e90d762008-10-16 16:46:07 +0100361 bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
Ben Dooksb999f0d2008-07-03 11:24:27 +0100362 else
363 return -EINVAL;
364
365 clk->parent = parent;
366
367 local_irq_save(flags);
368
369 tcfg1 = __raw_readl(S3C2410_TCFG1);
370 tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
371 __raw_writel(tcfg1 | bits, S3C2410_TCFG1);
372
373 local_irq_restore(flags);
374
375 return 0;
376}
377
378static struct clk clk_tin[] = {
379 [0] = {
380 .name = "pwm-tin",
381 .id = 0,
382 .set_parent = clk_pwm_tin_set_parent,
383 },
384 [1] = {
385 .name = "pwm-tin",
386 .id = 1,
387 .set_parent = clk_pwm_tin_set_parent,
388 },
389 [2] = {
390 .name = "pwm-tin",
391 .id = 2,
392 .set_parent = clk_pwm_tin_set_parent,
393 },
394 [3] = {
395 .name = "pwm-tin",
396 .id = 3,
397 .set_parent = clk_pwm_tin_set_parent,
398 },
399 [4] = {
400 .name = "pwm-tin",
401 .id = 4,
402 .set_parent = clk_pwm_tin_set_parent,
403 },
404};
405
406static __init int clk_pwm_tin_register(struct clk *pwm)
407{
408 unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
409 unsigned int id = pwm->id;
410
411 struct clk *parent;
412 int ret;
413
414 ret = s3c24xx_register_clock(pwm);
415 if (ret < 0)
416 return ret;
417
418 tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
419 tcfg1 &= S3C2410_TCFG1_MUX_MASK;
420
421 if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
422 parent = s3c24xx_pwmclk_tclk(id);
423 else
424 parent = s3c24xx_pwmclk_tdiv(id);
425
426 return clk_set_parent(pwm, parent);
427}
428
429static __init int s3c24xx_pwmclk_init(void)
430{
431 struct clk *clk_timers;
432 unsigned int clk;
433 int ret;
434
435 clk_timers = clk_get(NULL, "timers");
436 if (IS_ERR(clk_timers)) {
437 printk(KERN_ERR "%s: no parent clock\n", __func__);
438 return -EINVAL;
439 }
440
441 for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
442 clk_timer_scaler[clk].parent = clk_timers;
443 ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
444 if (ret < 0) {
445 printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
446 goto err;
447 }
448 }
449
450 for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) {
451 ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
452 if (ret < 0) {
453 printk(KERN_ERR "error adding pww tclk%d\n", clk);
454 goto err;
455 }
456 }
457
458 for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
459 ret = clk_pwm_tdiv_register(clk);
460 if (ret < 0) {
461 printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
462 goto err;
463 }
464 }
465
466 for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
467 ret = clk_pwm_tin_register(&clk_tin[clk]);
468 if (ret < 0) {
469 printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
470 goto err;
471 }
472 }
473
474 return 0;
475
476 err:
477 return ret;
478}
479
480arch_initcall(s3c24xx_pwmclk_init);