2 +++ b/arch/arm/plat-s3c/include/plat/pwm.h
4 +#ifndef __S3C2410_PWM_H
5 +#define __S3C2410_PWM_H
7 +#include <linux/err.h>
8 +#include <linux/platform_device.h>
9 +#include <linux/clk.h>
12 +#include <mach/hardware.h>
13 +#include <asm/mach-types.h>
14 +#include <plat/regs-timer.h>
25 + enum pwm_timer timerid;
27 + unsigned long pclk_rate;
28 + unsigned long prescaler;
29 + unsigned long divider;
30 + unsigned long counter;
31 + unsigned long comparer;
34 +struct s3c24xx_pwm_platform_data{
35 + /* callback to attach platform children (to enforce suspend / resume
37 + void (*attach_child_devices)(struct device *parent_device);
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);
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
53 Internal configuration for S3C DMA core
58 + PWM timer code for the S3C2410, and similar processors
60 # device definitions to compile in
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
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
73 +++ b/arch/arm/plat-s3c/pwm.c
76 + * arch/arm/plat-s3c/pwm.c
78 + * Copyright (c) by Javi Roman <javiroman@kernel-labs.org>
79 + * for the Openmoko Project.
81 + * S3C2410A SoC PWM support
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.
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
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>
104 + static unsigned long standby_reg_tcon;
105 + static unsigned long standby_reg_tcfg0;
106 + static unsigned long standby_reg_tcfg1;
109 +int s3c2410_pwm_disable(struct s3c2410_pwm *pwm)
111 + unsigned long tcon;
114 + tcon = __raw_readl(S3C2410_TCON);
115 + tcon &= 0xffffff00;
116 + __raw_writel(tcon, S3C2410_TCON);
118 + clk_disable(pwm->pclk);
119 + clk_put(pwm->pclk);
123 +EXPORT_SYMBOL_GPL(s3c2410_pwm_disable);
125 +int s3c2410_pwm_init(struct s3c2410_pwm *pwm)
127 + pwm->pclk = clk_get(NULL, "timers");
128 + if (IS_ERR(pwm->pclk))
129 + return PTR_ERR(pwm->pclk);
131 + clk_enable(pwm->pclk);
132 + pwm->pclk_rate = clk_get_rate(pwm->pclk);
135 +EXPORT_SYMBOL_GPL(s3c2410_pwm_init);
137 +int s3c2410_pwm_enable(struct s3c2410_pwm *pwm)
139 + unsigned long tcfg0, tcfg1, tcnt, tcmp;
141 + /* control registers bits */
142 + tcfg1 = __raw_readl(S3C2410_TCFG1);
143 + tcfg0 = __raw_readl(S3C2410_TCFG0);
145 + /* divider & scaler slection */
146 + switch (pwm->timerid) {
148 + tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
149 + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
152 + tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK;
153 + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
156 + tcfg1 &= ~S3C2410_TCFG1_MUX2_MASK;
157 + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
160 + tcfg1 &= ~S3C2410_TCFG1_MUX3_MASK;
161 + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
164 + /* timer four is not capable of doing PWM */
167 + clk_disable(pwm->pclk);
168 + clk_put(pwm->pclk);
172 + /* divider & scaler values */
173 + tcfg1 |= pwm->divider;
174 + __raw_writel(tcfg1, S3C2410_TCFG1);
176 + switch (pwm->timerid) {
179 + tcfg0 |= pwm->prescaler;
180 + __raw_writel(tcfg0, S3C2410_TCFG0);
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",
191 + /* timer count and compare buffer initial values */
192 + tcnt = pwm->counter;
193 + tcmp = pwm->comparer;
195 + __raw_writel(tcnt, S3C2410_TCNTB(pwm->timerid));
196 + __raw_writel(tcmp, S3C2410_TCMPB(pwm->timerid));
198 + /* ensure timer is stopped */
199 + s3c2410_pwm_stop(pwm);
203 +EXPORT_SYMBOL_GPL(s3c2410_pwm_enable);
205 +int s3c2410_pwm_start(struct s3c2410_pwm *pwm)
207 + unsigned long tcon;
209 + tcon = __raw_readl(S3C2410_TCON);
211 + switch (pwm->timerid) {
213 + tcon |= S3C2410_TCON_T0START;
214 + tcon &= ~S3C2410_TCON_T0MANUALUPD;
217 + tcon |= S3C2410_TCON_T1START;
218 + tcon &= ~S3C2410_TCON_T1MANUALUPD;
221 + tcon |= S3C2410_TCON_T2START;
222 + tcon &= ~S3C2410_TCON_T2MANUALUPD;
225 + tcon |= S3C2410_TCON_T3START;
226 + tcon &= ~S3C2410_TCON_T3MANUALUPD;
229 + /* timer four is not capable of doing PWM */
234 + __raw_writel(tcon, S3C2410_TCON);
238 +EXPORT_SYMBOL_GPL(s3c2410_pwm_start);
240 +int s3c2410_pwm_stop(struct s3c2410_pwm *pwm)
242 + unsigned long tcon;
244 + tcon = __raw_readl(S3C2410_TCON);
246 + switch (pwm->timerid) {
248 + tcon &= ~0x00000000;
249 + tcon |= S3C2410_TCON_T0RELOAD;
250 + tcon |= S3C2410_TCON_T0MANUALUPD;
253 + tcon &= ~0x00000080;
254 + tcon |= S3C2410_TCON_T1RELOAD;
255 + tcon |= S3C2410_TCON_T1MANUALUPD;
258 + tcon &= ~0x00000800;
259 + tcon |= S3C2410_TCON_T2RELOAD;
260 + tcon |= S3C2410_TCON_T2MANUALUPD;
263 + tcon &= ~0x00008000;
264 + tcon |= S3C2410_TCON_T3RELOAD;
265 + tcon |= S3C2410_TCON_T3MANUALUPD;
268 + /* timer four is not capable of doing PWM */
273 + __raw_writel(tcon, S3C2410_TCON);
277 +EXPORT_SYMBOL_GPL(s3c2410_pwm_stop);
279 +int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *pwm)
281 + __raw_writel(reg_value, S3C2410_TCMPB(pwm->timerid));
285 +EXPORT_SYMBOL_GPL(s3c2410_pwm_duty_cycle);
287 +int s3c2410_pwm_dumpregs(void)
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));
296 +EXPORT_SYMBOL_GPL(s3c2410_pwm_dumpregs);
298 +static int __init s3c24xx_pwm_probe(struct platform_device *pdev)
300 + struct s3c24xx_pwm_platform_data *pdata = pdev->dev.platform_data;
302 + dev_info(&pdev->dev, "s3c24xx_pwm is registered \n");
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
309 + if (pdata->attach_child_devices)
310 + (pdata->attach_child_devices)(&pdev->dev);
316 +static int s3c24xx_pwm_suspend(struct platform_device *pdev, pm_message_t state)
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);
326 +static int s3c24xx_pwm_resume(struct platform_device *pdev)
328 + __raw_writel(standby_reg_tcon, S3C2410_TCON);
329 + __raw_writel(standby_reg_tcfg0, S3C2410_TCFG0);
330 + __raw_writel(standby_reg_tcfg1, S3C2410_TCFG1);
335 +#define s3c24xx_pwm_suspend NULL
336 +#define s3c24xx_pwm_resume NULL
339 +static struct platform_driver s3c24xx_pwm_driver = {
341 + .name = "s3c24xx_pwm",
342 + .owner = THIS_MODULE,
344 + .probe = s3c24xx_pwm_probe,
345 + .suspend = s3c24xx_pwm_suspend,
346 + .resume = s3c24xx_pwm_resume,
349 +static int __init s3c24xx_pwm_init(void)
351 + return platform_driver_register(&s3c24xx_pwm_driver);
354 +static void __exit s3c24xx_pwm_exit(void)
358 +MODULE_AUTHOR("Javi Roman <javiroman@kernel-labs.org>");
359 +MODULE_LICENSE("GPL");
361 +module_init(s3c24xx_pwm_init);
362 +module_exit(s3c24xx_pwm_exit);
364 +++ b/arch/arm/plat-s3c24xx/pwm-clock.c
366 +/* linux/arch/arm/plat-s3c24xx/pwm-clock.c
368 + * Copyright (c) 2007 Simtec Electronics
369 + * Copyright (c) 2007, 2008 Ben Dooks
370 + * Ben Dooks <ben-linux@fluff.org>
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.
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>
386 +#include <mach/hardware.h>
387 +#include <asm/irq.h>
389 +#include <mach/regs-clock.h>
390 +#include <mach/regs-gpio.h>
392 +#include <asm/plat-s3c24xx/clock.h>
393 +#include <asm/plat-s3c24xx/cpu.h>
395 +#include <asm/plat-s3c/regs-timer.h>
397 +/* Each of the timers 0 through 5 go through the following
398 + * clock tree, with the inputs depending on the timers.
400 + * pclk ---- [ prescaler 0 ] -+---> timer 0
403 + * pclk ---- [ prescaler 1 ] -+---> timer 2
407 + * Which are fed into the timers as so:
409 + * prescaled 0 ---- [ div 2,4,8,16 ] ---\
411 + * tclk 0 ------------------------------/
413 + * prescaled 0 ---- [ div 2,4,8,16 ] ---\
415 + * tclk 0 ------------------------------/
418 + * prescaled 1 ---- [ div 2,4,8,16 ] ---\
420 + * tclk 1 ------------------------------/
422 + * prescaled 1 ---- [ div 2,4,8,16 ] ---\
424 + * tclk 1 ------------------------------/
426 + * prescaled 1 ---- [ div 2,4,8, 16 ] --\
428 + * tclk 1 ------------------------------/
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.
436 + * prescaled clk --> pwm-tdiv ---\
437 + * [ mux ] --> timer X
438 + * tclk -------------------------/
441 +static unsigned long clk_pwm_scaler_getrate(struct clk *clk)
443 + unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
445 + if (clk->id == 1) {
446 + tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
447 + tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
449 + tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK;
452 + return clk_get_rate(clk->parent) / (tcfg0 + 1);
455 +/* TODO - add set rate calls. */
457 +static struct clk clk_timer_scaler[] = {
459 + .name = "pwm-scaler0",
461 + .get_rate = clk_pwm_scaler_getrate,
464 + .name = "pwm-scaler1",
466 + .get_rate = clk_pwm_scaler_getrate,
470 +static struct clk clk_timer_tclk[] = {
472 + .name = "pwm-tclk0",
476 + .name = "pwm-tclk1",
481 +struct pwm_tdiv_clk {
483 + unsigned int divisor;
486 +static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
488 + return container_of(clk, struct pwm_tdiv_clk, clk);
491 +static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
493 + return 1 << (1 + tcfg1);
496 +static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
498 + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
499 + unsigned int divisor;
501 + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
502 + tcfg1 &= S3C2410_TCFG1_MUX_MASK;
504 + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
505 + divisor = to_tdiv(clk)->divisor;
507 + divisor = tcfg_to_divisor(tcfg1);
509 + return clk_get_rate(clk->parent) / divisor;
512 +static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
513 + unsigned long rate)
515 + unsigned long parent_rate;
516 + unsigned long divisor;
518 + parent_rate = clk_get_rate(clk->parent);
519 + divisor = parent_rate / rate;
523 + else if (divisor <= 4)
525 + else if (divisor <= 8)
530 + return parent_rate / divisor;
533 +static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
535 + unsigned long bits;
537 + switch (divclk->divisor) {
539 + bits = S3C2410_TCFG1_MUX_DIV2;
542 + bits = S3C2410_TCFG1_MUX_DIV4;
545 + bits = S3C2410_TCFG1_MUX_DIV8;
549 + bits = S3C2410_TCFG1_MUX_DIV16;
556 +static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
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);
563 + local_irq_save(flags);
565 + tcfg1 = __raw_readl(S3C2410_TCFG1);
566 + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
567 + tcfg1 |= bits << shift;
568 + __raw_writel(tcfg1, S3C2410_TCFG1);
570 + local_irq_restore(flags);
573 +static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
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;
580 + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
581 + tcfg1 &= S3C2410_TCFG1_MUX_MASK;
583 + rate = clk_round_rate(clk, rate);
584 + divisor = parent_rate / rate;
589 + divclk->divisor = divisor;
591 + /* Update the current MUX settings if we are currently
592 + * selected as the clock source for this clock. */
594 + if (tcfg1 != S3C2410_TCFG1_MUX_TCLK)
595 + clk_pwm_tdiv_update(divclk);
600 +static struct pwm_tdiv_clk clk_timer_tdiv[] = {
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,
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,
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,
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,
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,
648 +static int __init clk_pwm_tdiv_register(unsigned int id)
650 + struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
651 + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
653 + tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
654 + tcfg1 &= S3C2410_TCFG1_MUX_MASK;
656 + divclk->clk.id = id;
657 + divclk->divisor = tcfg_to_divisor(tcfg1);
659 + return s3c24xx_register_clock(&divclk->clk);
662 +static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
664 + return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
667 +static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
669 + return &clk_timer_tdiv[id].clk;
672 +static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
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);
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;
687 + clk->parent = parent;
689 + local_irq_save(flags);
691 + tcfg1 = __raw_readl(S3C2410_TCFG1);
692 + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
693 + __raw_writel(tcfg1 | bits, S3C2410_TCFG1);
695 + local_irq_restore(flags);
700 +static struct clk clk_tin[] = {
704 + .set_parent = clk_pwm_tin_set_parent,
709 + .set_parent = clk_pwm_tin_set_parent,
714 + .set_parent = clk_pwm_tin_set_parent,
719 + .set_parent = clk_pwm_tin_set_parent,
724 + .set_parent = clk_pwm_tin_set_parent,
728 +static __init int clk_pwm_tin_register(struct clk *pwm)
730 + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
731 + unsigned int id = pwm->id;
733 + struct clk *parent;
736 + ret = s3c24xx_register_clock(pwm);
740 + tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
741 + tcfg1 &= S3C2410_TCFG1_MUX_MASK;
743 + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
744 + parent = s3c24xx_pwmclk_tclk(id);
746 + parent = s3c24xx_pwmclk_tdiv(id);
748 + return clk_set_parent(pwm, parent);
751 +static __init int s3c24xx_pwmclk_init(void)
753 + struct clk *clk_timers;
757 + clk_timers = clk_get(NULL, "timers");
758 + if (IS_ERR(clk_timers)) {
759 + printk(KERN_ERR "%s: no parent clock\n", __func__);
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]);
767 + printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
772 + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) {
773 + ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
775 + printk(KERN_ERR "error adding pww tclk%d\n", clk);
780 + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
781 + ret = clk_pwm_tdiv_register(clk);
783 + printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
788 + for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
789 + ret = clk_pwm_tin_register(&clk_tin[clk]);
791 + printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
802 +arch_initcall(s3c24xx_pwmclk_init);