goldfish: R.I.P.
[openwrt.git] / target / linux / s3c24xx / patches-2.6.30 / 011-s3c-pwm.patch
1 --- /dev/null
2 +++ b/arch/arm/plat-s3c/include/plat/pwm.h
3 @@ -0,0 +1,45 @@
4 +#ifndef __S3C2410_PWM_H
5 +#define __S3C2410_PWM_H
6 +
7 +#include <linux/err.h>
8 +#include <linux/platform_device.h>
9 +#include <linux/clk.h>
10 +
11 +#include <mach/io.h>
12 +#include <mach/hardware.h>
13 +#include <asm/mach-types.h>
14 +#include <plat/regs-timer.h>
15 +
16 +enum pwm_timer {
17 +       PWM0,
18 +       PWM1,
19 +       PWM2,
20 +       PWM3,
21 +       PWM4
22 +};
23 +
24 +struct s3c2410_pwm {
25 +       enum pwm_timer timerid;
26 +       struct clk *pclk;
27 +       unsigned long pclk_rate;
28 +       unsigned long prescaler;
29 +       unsigned long divider;
30 +       unsigned long counter;
31 +       unsigned long comparer;
32 +};
33 +
34 +struct s3c24xx_pwm_platform_data{
35 +        /* callback to attach platform children (to enforce suspend / resume
36 +         * ordering */
37 +        void (*attach_child_devices)(struct device *parent_device);
38 +};
39 +
40 +int s3c2410_pwm_init(struct s3c2410_pwm *s3c2410_pwm);
41 +int s3c2410_pwm_enable(struct s3c2410_pwm *s3c2410_pwm);
42 +int s3c2410_pwm_disable(struct s3c2410_pwm *s3c2410_pwm);
43 +int s3c2410_pwm_start(struct s3c2410_pwm *s3c2410_pwm);
44 +int s3c2410_pwm_stop(struct s3c2410_pwm *s3c2410_pwm);
45 +int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *s3c2410_pwm);
46 +int s3c2410_pwm_dumpregs(void);
47 +
48 +#endif /* __S3C2410_PWM_H */
49 --- a/arch/arm/plat-s3c/Kconfig
50 +++ b/arch/arm/plat-s3c/Kconfig
51 @@ -157,6 +157,11 @@ config S3C_DMA
52         help
53           Internal configuration for S3C DMA core
54  
55 +config S3C_PWM
56 +       bool
57 +       help
58 +         PWM timer code for the S3C2410, and similar processors
59 +
60  # device definitions to compile in
61  
62  config S3C_DEV_HSMMC
63 --- a/arch/arm/plat-s3c/Makefile
64 +++ b/arch/arm/plat-s3c/Makefile
65 @@ -35,5 +35,6 @@ obj-$(CONFIG_S3C_DEV_HSMMC1)  += dev-hsmm
66  obj-y                          += dev-i2c0.o
67  obj-$(CONFIG_S3C_DEV_I2C1)     += dev-i2c1.o
68  obj-$(CONFIG_S3C_DEV_FB)       += dev-fb.o
69 +obj-$(CONFIG_S3C_PWM)          += pwm.o
70  obj-$(CONFIG_S3C_DMA)          += dma.o
71  
72 --- /dev/null
73 +++ b/arch/arm/plat-s3c/pwm.c
74 @@ -0,0 +1,288 @@
75 +/*
76 + * arch/arm/plat-s3c/pwm.c
77 + *
78 + * Copyright (c) by Javi Roman <javiroman@kernel-labs.org>
79 + *              for the Openmoko Project.
80 + *
81 + *     S3C2410A SoC PWM support
82 + *
83 + * This program is free software; you can redistribute it and/or modify
84 + * it under the terms of the GNU General Public License as published by
85 + * the Free Software Foundation; either version 2 of the License, or
86 + * (at your option) any later version.
87 + *
88 + * You should have received a copy of the GNU General Public License
89 + * along with this program; if not, write to the Free Software
90 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
91 + *
92 + */
93 +
94 +#include <linux/kernel.h>
95 +#include <linux/init.h>
96 +#include <linux/clk.h>
97 +#include <linux/device.h>
98 +#include <mach/hardware.h>
99 +#include <plat/regs-timer.h>
100 +#include <plat/pwm.h>
101 +#include <asm/io.h>
102 +
103 +#ifdef CONFIG_PM
104 +       static unsigned long standby_reg_tcon;
105 +       static unsigned long standby_reg_tcfg0;
106 +       static unsigned long standby_reg_tcfg1;
107 +#endif
108 +
109 +int s3c2410_pwm_disable(struct s3c2410_pwm *pwm)
110 +{
111 +       unsigned long tcon;
112 +
113 +       /* stop timer */
114 +       tcon = __raw_readl(S3C2410_TCON);
115 +       tcon &= 0xffffff00;
116 +       __raw_writel(tcon, S3C2410_TCON);
117 +
118 +       clk_disable(pwm->pclk);
119 +       clk_put(pwm->pclk);
120 +
121 +       return 0;
122 +}
123 +EXPORT_SYMBOL_GPL(s3c2410_pwm_disable);
124 +
125 +int s3c2410_pwm_init(struct s3c2410_pwm *pwm)
126 +{
127 +       pwm->pclk = clk_get(NULL, "timers");
128 +       if (IS_ERR(pwm->pclk))
129 +               return PTR_ERR(pwm->pclk);
130 +
131 +       clk_enable(pwm->pclk);
132 +       pwm->pclk_rate = clk_get_rate(pwm->pclk);
133 +       return 0;
134 +}
135 +EXPORT_SYMBOL_GPL(s3c2410_pwm_init);
136 +
137 +int s3c2410_pwm_enable(struct s3c2410_pwm *pwm)
138 +{
139 +       unsigned long tcfg0, tcfg1, tcnt, tcmp;
140 +
141 +       /* control registers bits */
142 +       tcfg1 = __raw_readl(S3C2410_TCFG1);
143 +       tcfg0 = __raw_readl(S3C2410_TCFG0);
144 +
145 +       /* divider & scaler slection */
146 +       switch (pwm->timerid) {
147 +       case PWM0:
148 +               tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
149 +               tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
150 +               break;
151 +       case PWM1:
152 +               tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK;
153 +               tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
154 +               break;
155 +       case PWM2:
156 +               tcfg1 &= ~S3C2410_TCFG1_MUX2_MASK;
157 +               tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
158 +               break;
159 +       case PWM3:
160 +               tcfg1 &= ~S3C2410_TCFG1_MUX3_MASK;
161 +               tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
162 +               break;
163 +       case PWM4:
164 +               /* timer four is not capable of doing PWM */
165 +               break;
166 +       default:
167 +               clk_disable(pwm->pclk);
168 +               clk_put(pwm->pclk);
169 +               return -1;
170 +       }
171 +
172 +       /* divider & scaler values */
173 +       tcfg1 |= pwm->divider;
174 +       __raw_writel(tcfg1, S3C2410_TCFG1);
175 +
176 +       switch (pwm->timerid) {
177 +       case PWM0:
178 +       case PWM1:
179 +               tcfg0 |= pwm->prescaler;
180 +               __raw_writel(tcfg0, S3C2410_TCFG0);
181 +               break;
182 +       default:
183 +               if ((tcfg0 | pwm->prescaler) != tcfg0) {
184 +                       printk(KERN_WARNING "not changing prescaler of PWM %u,"
185 +                              " since it's shared with timer4 (clock tick)\n",
186 +                              pwm->timerid);
187 +               }
188 +               break;
189 +       }
190 +
191 +       /* timer count and compare buffer initial values */
192 +       tcnt = pwm->counter;
193 +       tcmp = pwm->comparer;
194 +
195 +       __raw_writel(tcnt, S3C2410_TCNTB(pwm->timerid));
196 +       __raw_writel(tcmp, S3C2410_TCMPB(pwm->timerid));
197 +
198 +       /* ensure timer is stopped */
199 +       s3c2410_pwm_stop(pwm);
200 +
201 +       return 0;
202 +}
203 +EXPORT_SYMBOL_GPL(s3c2410_pwm_enable);
204 +
205 +int s3c2410_pwm_start(struct s3c2410_pwm *pwm)
206 +{
207 +       unsigned long tcon;
208 +
209 +       tcon = __raw_readl(S3C2410_TCON);
210 +
211 +       switch (pwm->timerid) {
212 +       case PWM0:
213 +               tcon |= S3C2410_TCON_T0START;
214 +               tcon &= ~S3C2410_TCON_T0MANUALUPD;
215 +               break;
216 +       case PWM1:
217 +               tcon |= S3C2410_TCON_T1START;
218 +               tcon &= ~S3C2410_TCON_T1MANUALUPD;
219 +               break;
220 +       case PWM2:
221 +               tcon |= S3C2410_TCON_T2START;
222 +               tcon &= ~S3C2410_TCON_T2MANUALUPD;
223 +               break;
224 +       case PWM3:
225 +               tcon |= S3C2410_TCON_T3START;
226 +               tcon &= ~S3C2410_TCON_T3MANUALUPD;
227 +               break;
228 +       case PWM4:
229 +               /* timer four is not capable of doing PWM */
230 +       default:
231 +               return -ENODEV;
232 +       }
233 +
234 +       __raw_writel(tcon, S3C2410_TCON);
235 +
236 +       return 0;
237 +}
238 +EXPORT_SYMBOL_GPL(s3c2410_pwm_start);
239 +
240 +int s3c2410_pwm_stop(struct s3c2410_pwm *pwm)
241 +{
242 +       unsigned long tcon;
243 +
244 +       tcon = __raw_readl(S3C2410_TCON);
245 +
246 +       switch (pwm->timerid) {
247 +       case PWM0:
248 +               tcon &= ~0x00000000;
249 +               tcon |= S3C2410_TCON_T0RELOAD;
250 +               tcon |= S3C2410_TCON_T0MANUALUPD;
251 +               break;
252 +       case PWM1:
253 +               tcon &= ~0x00000080;
254 +               tcon |= S3C2410_TCON_T1RELOAD;
255 +               tcon |= S3C2410_TCON_T1MANUALUPD;
256 +               break;
257 +       case PWM2:
258 +               tcon &= ~0x00000800;
259 +               tcon |= S3C2410_TCON_T2RELOAD;
260 +               tcon |= S3C2410_TCON_T2MANUALUPD;
261 +               break;
262 +       case PWM3:
263 +               tcon &= ~0x00008000;
264 +               tcon |= S3C2410_TCON_T3RELOAD;
265 +               tcon |= S3C2410_TCON_T3MANUALUPD;
266 +               break;
267 +       case PWM4:
268 +               /* timer four is not capable of doing PWM */
269 +       default:
270 +               return -ENODEV;
271 +       }
272 +
273 +       __raw_writel(tcon, S3C2410_TCON);
274 +
275 +       return 0;
276 +}
277 +EXPORT_SYMBOL_GPL(s3c2410_pwm_stop);
278 +
279 +int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *pwm)
280 +{
281 +       __raw_writel(reg_value, S3C2410_TCMPB(pwm->timerid));
282 +
283 +       return 0;
284 +}
285 +EXPORT_SYMBOL_GPL(s3c2410_pwm_duty_cycle);
286 +
287 +int s3c2410_pwm_dumpregs(void)
288 +{
289 +       printk(KERN_INFO "TCON: %08lx, TCFG0: %08lx, TCFG1: %08lx\n",
290 +                       (unsigned long) __raw_readl(S3C2410_TCON),
291 +                       (unsigned long) __raw_readl(S3C2410_TCFG0),
292 +                       (unsigned long) __raw_readl(S3C2410_TCFG1));
293 +
294 +       return 0;
295 +}
296 +EXPORT_SYMBOL_GPL(s3c2410_pwm_dumpregs);
297 +
298 +static int __init s3c24xx_pwm_probe(struct platform_device *pdev)
299 +{
300 +       struct s3c24xx_pwm_platform_data *pdata = pdev->dev.platform_data;
301 +
302 +       dev_info(&pdev->dev, "s3c24xx_pwm is registered \n");
303 +
304 +       /* if platform was interested, give him a chance to register
305 +        * platform devices that switch power with us as the parent
306 +        * at registration time -- ensures suspend / resume ordering
307 +        */
308 +       if (pdata)
309 +               if (pdata->attach_child_devices)
310 +                       (pdata->attach_child_devices)(&pdev->dev);
311 +
312 +       return 0;
313 +}
314 +
315 +#ifdef CONFIG_PM
316 +static int s3c24xx_pwm_suspend(struct platform_device *pdev, pm_message_t state)
317 +{
318 +       /* PWM config should be kept in suspending */
319 +       standby_reg_tcon = __raw_readl(S3C2410_TCON);
320 +       standby_reg_tcfg0 = __raw_readl(S3C2410_TCFG0);
321 +       standby_reg_tcfg1 = __raw_readl(S3C2410_TCFG1);
322 +
323 +       return 0;
324 +}
325 +
326 +static int s3c24xx_pwm_resume(struct platform_device *pdev)
327 +{
328 +       __raw_writel(standby_reg_tcon, S3C2410_TCON);
329 +       __raw_writel(standby_reg_tcfg0, S3C2410_TCFG0);
330 +       __raw_writel(standby_reg_tcfg1, S3C2410_TCFG1);
331 +
332 +       return 0;
333 +}
334 +#else
335 +#define s3c24xx_pwm_suspend    NULL
336 +#define s3c24xx_pwm_resume     NULL
337 +#endif
338 +
339 +static struct platform_driver s3c24xx_pwm_driver = {
340 +       .driver = {
341 +               .name   = "s3c24xx_pwm",
342 +               .owner  = THIS_MODULE,
343 +       },
344 +       .probe   = s3c24xx_pwm_probe,
345 +       .suspend = s3c24xx_pwm_suspend,
346 +       .resume  = s3c24xx_pwm_resume,
347 +};
348 +
349 +static int __init s3c24xx_pwm_init(void)
350 +{
351 +       return platform_driver_register(&s3c24xx_pwm_driver);
352 +}
353 +
354 +static void __exit s3c24xx_pwm_exit(void)
355 +{
356 +}
357 +
358 +MODULE_AUTHOR("Javi Roman <javiroman@kernel-labs.org>");
359 +MODULE_LICENSE("GPL");
360 +
361 +module_init(s3c24xx_pwm_init);
362 +module_exit(s3c24xx_pwm_exit);
363 --- /dev/null
364 +++ b/arch/arm/plat-s3c24xx/pwm-clock.c
365 @@ -0,0 +1,437 @@
366 +/* linux/arch/arm/plat-s3c24xx/pwm-clock.c
367 + *
368 + * Copyright (c) 2007 Simtec Electronics
369 + * Copyright (c) 2007, 2008 Ben Dooks
370 + *     Ben Dooks <ben-linux@fluff.org>
371 + *
372 + * This program is free software; you can redistribute it and/or modify
373 + * it under the terms of the GNU General Public License as published by
374 + * the Free Software Foundation; either version 2 of the License.
375 +*/
376 +
377 +#include <linux/init.h>
378 +#include <linux/module.h>
379 +#include <linux/kernel.h>
380 +#include <linux/list.h>
381 +#include <linux/errno.h>
382 +#include <linux/clk.h>
383 +#include <linux/err.h>
384 +#include <linux/io.h>
385 +
386 +#include <mach/hardware.h>
387 +#include <asm/irq.h>
388 +
389 +#include <mach/regs-clock.h>
390 +#include <mach/regs-gpio.h>
391 +
392 +#include <asm/plat-s3c24xx/clock.h>
393 +#include <asm/plat-s3c24xx/cpu.h>
394 +
395 +#include <asm/plat-s3c/regs-timer.h>
396 +
397 +/* Each of the timers 0 through 5 go through the following
398 + * clock tree, with the inputs depending on the timers.
399 + *
400 + * pclk ---- [ prescaler 0 ] -+---> timer 0
401 + *                           +---> timer 1
402 + *
403 + * pclk ---- [ prescaler 1 ] -+---> timer 2
404 + *                           +---> timer 3
405 + *                           \---> timer 4
406 + *
407 + * Which are fed into the timers as so:
408 + *
409 + * prescaled 0 ---- [ div 2,4,8,16 ] ---\
410 + *                                    [mux] -> timer 0
411 + * tclk 0 ------------------------------/
412 + *
413 + * prescaled 0 ---- [ div 2,4,8,16 ] ---\
414 + *                                    [mux] -> timer 1
415 + * tclk 0 ------------------------------/
416 + *
417 + *
418 + * prescaled 1 ---- [ div 2,4,8,16 ] ---\
419 + *                                    [mux] -> timer 2
420 + * tclk 1 ------------------------------/
421 + *
422 + * prescaled 1 ---- [ div 2,4,8,16 ] ---\
423 + *                                    [mux] -> timer 3
424 + * tclk 1 ------------------------------/
425 + *
426 + * prescaled 1 ---- [ div 2,4,8, 16 ] --\
427 + *                                    [mux] -> timer 4
428 + * tclk 1 ------------------------------/
429 + *
430 + * Since the mux and the divider are tied together in the
431 + * same register space, it is impossible to set the parent
432 + * and the rate at the same time. To avoid this, we add an
433 + * intermediate 'prescaled-and-divided' clock to select
434 + * as the parent for the timer input clock called tdiv.
435 + *
436 + * prescaled clk --> pwm-tdiv ---\
437 + *                             [ mux ] --> timer X
438 + * tclk -------------------------/
439 +*/
440 +
441 +static unsigned long clk_pwm_scaler_getrate(struct clk *clk)
442 +{
443 +       unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
444 +
445 +       if (clk->id == 1) {
446 +               tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
447 +               tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
448 +       } else {
449 +               tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK;
450 +       }
451 +
452 +       return clk_get_rate(clk->parent) / (tcfg0 + 1);
453 +}
454 +
455 +/* TODO - add set rate calls. */
456 +
457 +static struct clk clk_timer_scaler[] = {
458 +       [0]     = {
459 +               .name           = "pwm-scaler0",
460 +               .id             = -1,
461 +               .get_rate       = clk_pwm_scaler_getrate,
462 +       },
463 +       [1]     = {
464 +               .name           = "pwm-scaler1",
465 +               .id             = -1,
466 +               .get_rate       = clk_pwm_scaler_getrate,
467 +       },
468 +};
469 +
470 +static struct clk clk_timer_tclk[] = {
471 +       [0]     = {
472 +               .name           = "pwm-tclk0",
473 +               .id             = -1,
474 +       },
475 +       [1]     = {
476 +               .name           = "pwm-tclk1",
477 +               .id             = -1,
478 +       },
479 +};
480 +
481 +struct pwm_tdiv_clk {
482 +       struct clk      clk;
483 +       unsigned int    divisor;
484 +};
485 +
486 +static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
487 +{
488 +       return container_of(clk, struct pwm_tdiv_clk, clk);
489 +}
490 +
491 +static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
492 +{
493 +       return 1 << (1 + tcfg1);
494 +}
495 +
496 +static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
497 +{
498 +       unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
499 +       unsigned int divisor;
500 +
501 +       tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
502 +       tcfg1 &= S3C2410_TCFG1_MUX_MASK;
503 +
504 +       if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
505 +               divisor = to_tdiv(clk)->divisor;
506 +       else
507 +               divisor = tcfg_to_divisor(tcfg1);
508 +
509 +       return clk_get_rate(clk->parent) / divisor;
510 +}
511 +
512 +static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
513 +                                            unsigned long rate)
514 +{
515 +       unsigned long parent_rate;
516 +       unsigned long divisor;
517 +
518 +       parent_rate = clk_get_rate(clk->parent);
519 +       divisor = parent_rate / rate;
520 +
521 +       if (divisor <= 2)
522 +               divisor = 2;
523 +       else if (divisor <= 4)
524 +               divisor = 4;
525 +       else if (divisor <= 8)
526 +               divisor = 8;
527 +       else
528 +               divisor = 16;
529 +
530 +       return parent_rate / divisor;
531 +}
532 +
533 +static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
534 +{
535 +       unsigned long bits;
536 +
537 +       switch (divclk->divisor) {
538 +       case 2:
539 +               bits = S3C2410_TCFG1_MUX_DIV2;
540 +               break;
541 +       case 4:
542 +               bits = S3C2410_TCFG1_MUX_DIV4;
543 +               break;
544 +       case 8:
545 +               bits = S3C2410_TCFG1_MUX_DIV8;
546 +               break;
547 +       case 16:
548 +       default:
549 +               bits = S3C2410_TCFG1_MUX_DIV16;
550 +               break;
551 +       }
552 +
553 +       return bits;
554 +}
555 +
556 +static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
557 +{
558 +       unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
559 +       unsigned long bits = clk_pwm_tdiv_bits(divclk);
560 +       unsigned long flags;
561 +       unsigned long shift =  S3C2410_TCFG1_SHIFT(divclk->clk.id);
562 +
563 +       local_irq_save(flags);
564 +
565 +       tcfg1 = __raw_readl(S3C2410_TCFG1);
566 +       tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
567 +       tcfg1 |= bits << shift;
568 +       __raw_writel(tcfg1, S3C2410_TCFG1);
569 +
570 +       local_irq_restore(flags);
571 +}
572 +
573 +static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
574 +{
575 +       struct pwm_tdiv_clk *divclk = to_tdiv(clk);
576 +       unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
577 +       unsigned long parent_rate = clk_get_rate(clk->parent);
578 +       unsigned long divisor;
579 +
580 +       tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
581 +       tcfg1 &= S3C2410_TCFG1_MUX_MASK;
582 +
583 +       rate = clk_round_rate(clk, rate);
584 +       divisor = parent_rate / rate;
585 +
586 +       if (divisor > 16)
587 +               return -EINVAL;
588 +
589 +       divclk->divisor = divisor;
590 +
591 +       /* Update the current MUX settings if we are currently
592 +        * selected as the clock source for this clock. */
593 +
594 +       if (tcfg1 != S3C2410_TCFG1_MUX_TCLK)
595 +               clk_pwm_tdiv_update(divclk);
596 +
597 +       return 0;
598 +}
599 +
600 +static struct pwm_tdiv_clk clk_timer_tdiv[] = {
601 +       [0]     = {
602 +               .clk    = {
603 +                       .name           = "pwm-tdiv",
604 +                       .parent         = &clk_timer_scaler[0],
605 +                       .get_rate       = clk_pwm_tdiv_get_rate,
606 +                       .set_rate       = clk_pwm_tdiv_set_rate,
607 +                       .round_rate     = clk_pwm_tdiv_round_rate,
608 +               },
609 +       },
610 +       [1]     = {
611 +               .clk    = {
612 +                       .name           = "pwm-tdiv",
613 +                       .parent         = &clk_timer_scaler[0],
614 +                       .get_rate       = clk_pwm_tdiv_get_rate,
615 +                       .set_rate       = clk_pwm_tdiv_set_rate,
616 +                       .round_rate     = clk_pwm_tdiv_round_rate,
617 +               }
618 +       },
619 +       [2]     = {
620 +               .clk    = {
621 +                       .name           = "pwm-tdiv",
622 +                       .parent         = &clk_timer_scaler[1],
623 +                       .get_rate       = clk_pwm_tdiv_get_rate,
624 +                       .set_rate       = clk_pwm_tdiv_set_rate,
625 +                       .round_rate     = clk_pwm_tdiv_round_rate,
626 +               },
627 +       },
628 +       [3]     = {
629 +               .clk    = {
630 +                       .name           = "pwm-tdiv",
631 +                       .parent         = &clk_timer_scaler[1],
632 +                       .get_rate       = clk_pwm_tdiv_get_rate,
633 +                       .set_rate       = clk_pwm_tdiv_set_rate,
634 +                       .round_rate     = clk_pwm_tdiv_round_rate,
635 +               },
636 +       },
637 +       [4]     = {
638 +               .clk    = {
639 +                       .name           = "pwm-tdiv",
640 +                       .parent         = &clk_timer_scaler[1],
641 +                       .get_rate       = clk_pwm_tdiv_get_rate,
642 +                       .set_rate       = clk_pwm_tdiv_set_rate,
643 +                       .round_rate     = clk_pwm_tdiv_round_rate,
644 +               },
645 +       },
646 +};
647 +
648 +static int __init clk_pwm_tdiv_register(unsigned int id)
649 +{
650 +       struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
651 +       unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
652 +
653 +       tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
654 +       tcfg1 &= S3C2410_TCFG1_MUX_MASK;
655 +
656 +       divclk->clk.id = id;
657 +       divclk->divisor = tcfg_to_divisor(tcfg1);
658 +
659 +       return s3c24xx_register_clock(&divclk->clk);
660 +}
661 +
662 +static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
663 +{
664 +       return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
665 +}
666 +
667 +static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
668 +{
669 +       return &clk_timer_tdiv[id].clk;
670 +}
671 +
672 +static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
673 +{
674 +       unsigned int id = clk->id;
675 +       unsigned long tcfg1;
676 +       unsigned long flags;
677 +       unsigned long bits;
678 +       unsigned long shift = S3C2410_TCFG1_SHIFT(id);
679 +
680 +       if (parent == s3c24xx_pwmclk_tclk(id))
681 +               bits = S3C2410_TCFG1_MUX_TCLK << shift;
682 +       else if (parent == s3c24xx_pwmclk_tdiv(id))
683 +               bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
684 +       else
685 +               return -EINVAL;
686 +
687 +       clk->parent = parent;
688 +
689 +       local_irq_save(flags);
690 +
691 +       tcfg1 = __raw_readl(S3C2410_TCFG1);
692 +       tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
693 +       __raw_writel(tcfg1 | bits, S3C2410_TCFG1);
694 +
695 +       local_irq_restore(flags);
696 +
697 +       return 0;
698 +}
699 +
700 +static struct clk clk_tin[] = {
701 +       [0]     = {
702 +               .name           = "pwm-tin",
703 +               .id             = 0,
704 +               .set_parent     = clk_pwm_tin_set_parent,
705 +       },
706 +       [1]     = {
707 +               .name           = "pwm-tin",
708 +               .id             = 1,
709 +               .set_parent     = clk_pwm_tin_set_parent,
710 +       },
711 +       [2]     = {
712 +               .name           = "pwm-tin",
713 +               .id             = 2,
714 +               .set_parent     = clk_pwm_tin_set_parent,
715 +       },
716 +       [3]     = {
717 +               .name           = "pwm-tin",
718 +               .id             = 3,
719 +               .set_parent     = clk_pwm_tin_set_parent,
720 +       },
721 +       [4]     = {
722 +               .name           = "pwm-tin",
723 +               .id             = 4,
724 +               .set_parent     = clk_pwm_tin_set_parent,
725 +       },
726 +};
727 +
728 +static __init int clk_pwm_tin_register(struct clk *pwm)
729 +{
730 +       unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
731 +       unsigned int id = pwm->id;
732 +
733 +       struct clk *parent;
734 +       int ret;
735 +
736 +       ret = s3c24xx_register_clock(pwm);
737 +       if (ret < 0)
738 +               return ret;
739 +
740 +       tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
741 +       tcfg1 &= S3C2410_TCFG1_MUX_MASK;
742 +
743 +       if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
744 +               parent = s3c24xx_pwmclk_tclk(id);
745 +       else
746 +               parent = s3c24xx_pwmclk_tdiv(id);
747 +
748 +       return clk_set_parent(pwm, parent);
749 +}
750 +
751 +static __init int s3c24xx_pwmclk_init(void)
752 +{
753 +       struct clk *clk_timers;
754 +       unsigned int clk;
755 +       int ret;
756 +
757 +       clk_timers = clk_get(NULL, "timers");
758 +       if (IS_ERR(clk_timers)) {
759 +               printk(KERN_ERR "%s: no parent clock\n", __func__);
760 +               return -EINVAL;
761 +       }
762 +
763 +       for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
764 +               clk_timer_scaler[clk].parent = clk_timers;
765 +               ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
766 +               if (ret < 0) {
767 +                       printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
768 +                       goto err;
769 +               }
770 +       }
771 +
772 +       for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) {
773 +               ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
774 +               if (ret < 0) {
775 +                       printk(KERN_ERR "error adding pww tclk%d\n", clk);
776 +                       goto err;
777 +               }
778 +       }
779 +
780 +       for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
781 +               ret = clk_pwm_tdiv_register(clk);
782 +               if (ret < 0) {
783 +                       printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
784 +                       goto err;
785 +               }
786 +       }
787 +
788 +       for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
789 +               ret = clk_pwm_tin_register(&clk_tin[clk]);
790 +               if (ret < 0) {
791 +                       printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
792 +                       goto err;
793 +               }
794 +       }
795 +
796 +       return 0;
797 +
798 + err:
799 +       return ret;
800 +}
801 +
802 +arch_initcall(s3c24xx_pwmclk_init);