Index: linux-2.6.22/arch/arm/mach-s3c2410/Kconfig =================================================================== --- linux-2.6.22.orig/arch/arm/mach-s3c2410/Kconfig +++ linux-2.6.22/arch/arm/mach-s3c2410/Kconfig @@ -9,6 +9,7 @@ depends on ARCH_S3C2410 select S3C2410_CLOCK select S3C2410_GPIO + select S3C2410_PWM select CPU_LLSERIAL_S3C2410 select S3C2410_PM if PM help @@ -37,6 +38,11 @@ help Clock code for the S3C2410, and similar processors +config S3C2410_PWM + bool + help + PWM timer code for the S3C2410, and similar processors + menu "S3C2410 Machines" Index: linux-2.6.22/arch/arm/mach-s3c2410/Makefile =================================================================== --- linux-2.6.22.orig/arch/arm/mach-s3c2410/Makefile +++ linux-2.6.22/arch/arm/mach-s3c2410/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_S3C2410_PM) += pm.o sleep.o obj-$(CONFIG_S3C2410_GPIO) += gpio.o obj-$(CONFIG_S3C2410_CLOCK) += clock.o +obj-$(CONFIG_S3C2410_PWM) += pwm.o # Machine support Index: linux-2.6.22/arch/arm/mach-s3c2410/pwm.c =================================================================== --- /dev/null +++ linux-2.6.22/arch/arm/mach-s3c2410/pwm.c @@ -0,0 +1,222 @@ +/* + * arch/arm/mach-s3c2410/3c2410-pwm.c + * + * Copyright (c) by Javi Roman + * for the OpenMoko Project. + * + * S3C2410A SoC PWM support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +struct s3c2410_pwm_t *s3c2410_pwm_alloc(void) +{ + struct s3c2410_pwm_t *s3c2410_pwm; + + s3c2410_pwm = kzalloc(sizeof(*s3c2410_pwm), GFP_KERNEL); + if (!s3c2410_pwm) + /*return -ENOMEM;*/ + return NULL; + + s3c2410_pwm->pclk = clk_get(NULL, "timers"); + + if (IS_ERR(s3c2410_pwm->pclk)) { + /*return PTR_ERR(clk);*/ + kfree(s3c2410_pwm); + return NULL; + } + + clk_enable(s3c2410_pwm->pclk); + + s3c2410_pwm->pclk_rate = clk_get_rate(s3c2410_pwm->pclk); + + return s3c2410_pwm; +} + +EXPORT_SYMBOL_GPL(s3c2410_pwm_alloc); + +int s3c2410_pwm_disable(struct s3c2410_pwm_t *s3c2410_pwm) +{ + unsigned long tcon; + + /* stop timer */ + tcon = __raw_readl(S3C2410_TCON); + tcon &= 0xffffff00; + __raw_writel(tcon, S3C2410_TCON); + + clk_disable(s3c2410_pwm->pclk); + clk_put(s3c2410_pwm->pclk); + + kfree(s3c2410_pwm); + return 0; +} + +EXPORT_SYMBOL_GPL(s3c2410_pwm_disable); + +int s3c2410_pwm_enable(struct s3c2410_pwm_t *s3c2410_pwm) +{ + unsigned long tcfg0, tcfg1, tcnt, tcmp; + + /* control registers bits */ + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg0 = __raw_readl(S3C2410_TCFG0); + + /* divider & scaler slection */ + switch(s3c2410_pwm->timerid) { + case PWM0: + tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; + break; + case PWM1: + tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; + break; + case PWM2: + tcfg1 &= ~S3C2410_TCFG1_MUX2_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + break; + case PWM3: + tcfg1 &= ~S3C2410_TCFG1_MUX3_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + break; + case PWM4: + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + break; + default: + return -1; + } + + /* divider & scaler values */ + tcfg1 |= s3c2410_pwm->divider; + tcfg0 |= s3c2410_pwm->prescaler; + + __raw_writel(tcfg1, S3C2410_TCFG1); + __raw_writel(tcfg0, S3C2410_TCFG0); + + /* timer count and compare buffer initial values */ + tcnt = s3c2410_pwm->counter; + tcmp = s3c2410_pwm->comparer; + + __raw_writel(tcnt, S3C2410_TCNTB(s3c2410_pwm->timerid)); + __raw_writel(tcmp, S3C2410_TCMPB(s3c2410_pwm->timerid)); + + /* ensure timer is stopped */ + s3c2410_pwm_stop(s3c2410_pwm); + + return 0; +} + +EXPORT_SYMBOL_GPL(s3c2410_pwm_enable); + +int s3c2410_pwm_start(struct s3c2410_pwm_t *s3c2410_pwm) +{ + unsigned long tcon; + + tcon = __raw_readl(S3C2410_TCON); + + switch (s3c2410_pwm->timerid) { + case PWM0: + tcon |= S3C2410_TCON_T0START; + tcon &= ~S3C2410_TCON_T0MANUALUPD; + break; + case PWM1: + tcon |= S3C2410_TCON_T1START; + tcon &= ~S3C2410_TCON_T1MANUALUPD; + break; + case PWM2: + tcon |= S3C2410_TCON_T2START; + tcon &= ~S3C2410_TCON_T2MANUALUPD; + break; + case PWM3: + tcon |= S3C2410_TCON_T3START; + tcon &= ~S3C2410_TCON_T3MANUALUPD; + break; + case PWM4: + tcon |= S3C2410_TCON_T4START; + tcon &= ~S3C2410_TCON_T4MANUALUPD; + } + + __raw_writel(tcon, S3C2410_TCON); + + return 0; +} + +EXPORT_SYMBOL_GPL(s3c2410_pwm_start); + +int s3c2410_pwm_stop(struct s3c2410_pwm_t *s3c2410_pwm) +{ + unsigned long tcon; + + tcon = __raw_readl(S3C2410_TCON); + + switch (s3c2410_pwm->timerid) { + case PWM0: + tcon &= ~0x00000000; + tcon |= S3C2410_TCON_T0RELOAD; + tcon |= S3C2410_TCON_T0MANUALUPD; + break; + case PWM1: + tcon &= ~0x00000080; + tcon |= S3C2410_TCON_T1RELOAD; + tcon |= S3C2410_TCON_T1MANUALUPD; + break; + case PWM2: + tcon &= ~0x00000800; + tcon |= S3C2410_TCON_T2RELOAD; + tcon |= S3C2410_TCON_T2MANUALUPD; + break; + case PWM3: + tcon &= ~0x00008000; + tcon |= S3C2410_TCON_T3RELOAD; + tcon |= S3C2410_TCON_T3MANUALUPD; + break; + case PWM4: + tcon &= ~0x00080000; + tcon |= S3C2410_TCON_T4RELOAD; + tcon |= S3C2410_TCON_T3MANUALUPD; + } + + __raw_writel(tcon, S3C2410_TCON); + + return 0; +} + +EXPORT_SYMBOL_GPL(s3c2410_pwm_stop); + +int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm_t *s3c2410_pwm) +{ + __raw_writel(reg_value, S3C2410_TCMPB(s3c2410_pwm->timerid)); + + return 0; +} + +EXPORT_SYMBOL_GPL(s3c2410_pwm_duty_cycle); + +int s3c2410_pwm_dumpregs(void) +{ + printk(KERN_INFO "TCON: %08lx, TCFG0: %08lx, TCFG1: %08lx\n", + (unsigned long) __raw_readl(S3C2410_TCON), + (unsigned long) __raw_readl(S3C2410_TCFG0), + (unsigned long) __raw_readl(S3C2410_TCFG1)); + + return 0; +} + +EXPORT_SYMBOL_GPL(s3c2410_pwm_dumpregs); + Index: linux-2.6.22/include/asm-arm/arch-s3c2410/pwm.h =================================================================== --- /dev/null +++ linux-2.6.22/include/asm-arm/arch-s3c2410/pwm.h @@ -0,0 +1,39 @@ +#ifndef __S3C2410_PWM_H +#define __S3C2410_PWM_H + +#include +#include +#include + +#include +#include +#include +#include + +enum pwm_timer { + PWM0, + PWM1, + PWM2, + PWM3, + PWM4 +}; + +struct s3c2410_pwm_t { + enum pwm_timer timerid; + struct clk *pclk; + unsigned long pclk_rate; + unsigned long prescaler; + unsigned long divider; + unsigned long counter; + unsigned long comparer; +}; + +struct s3c2410_pwm_t *s3c2410_pwm_alloc(void); +int s3c2410_pwm_enable(struct s3c2410_pwm_t *s3c2410_pwm); +int s3c2410_pwm_disable(struct s3c2410_pwm_t *s3c2410_pwm); +int s3c2410_pwm_start(struct s3c2410_pwm_t *s3c2410_pwm); +int s3c2410_pwm_stop(struct s3c2410_pwm_t *s3c2410_pwm); +int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm_t *s3c2410_pwm); +int s3c2410_pwm_dumpregs(void); + +#endif /* __S3C2410_PWM_H */