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