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