fe9a7c8156acac180aba31d6a175832c93b3aa43
[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);
This page took 0.07277 seconds and 3 git commands to generate.