From e9db29d62cbcef7974099c3a4deac69ad2a72190 Mon Sep 17 00:00:00 2001
From: mokopatches <mokopatches@openmoko.org>
Date: Fri, 4 Apr 2008 11:31:44 +0100
Subject: [PATCH] s3c2410-pwm.patch
 This patch adds a PWM api abstraction for the S3C2410 SoC

Signed-off-by: Javi Roman <javiroman@kernel-labs.org>
Signed-off-by: Harald Welte <laforge@openmoko.org>
---
 arch/arm/mach-s3c2410/Kconfig      |    6 +
 arch/arm/mach-s3c2410/Makefile     |    1 +
 arch/arm/mach-s3c2410/pwm.c        |  214 ++++++++++++++++++++++++++++++++++++
 include/asm-arm/arch-s3c2410/pwm.h |   40 +++++++
 4 files changed, 261 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-s3c2410/pwm.c
 create mode 100644 include/asm-arm/arch-s3c2410/pwm.h

diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig
index ff339e1..e42195f 100644
--- a/arch/arm/mach-s3c2410/Kconfig
+++ b/arch/arm/mach-s3c2410/Kconfig
@@ -9,6 +9,7 @@ config CPU_S3C2410
 	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 @@ config S3C2410_CLOCK
 	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"
 
diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile
index 8952a8d..bb577f4 100644
--- a/arch/arm/mach-s3c2410/Makefile
+++ b/arch/arm/mach-s3c2410/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_CPU_S3C2410_DMA)	+= dma.o
 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
 
diff --git a/arch/arm/mach-s3c2410/pwm.c b/arch/arm/mach-s3c2410/pwm.c
new file mode 100644
index 0000000..8a07359
--- /dev/null
+++ b/arch/arm/mach-s3c2410/pwm.c
@@ -0,0 +1,214 @@
+/*
+ * arch/arm/mach-s3c2410/3c2410-pwm.c
+ *
+ * Copyright (c) by Javi Roman <javiroman@kernel-labs.org>
+ * 		 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <asm/hardware.h>
+#include <asm/plat-s3c/regs-timer.h>
+#include <asm/arch/pwm.h>
+
+int s3c2410_pwm_disable(struct s3c2410_pwm *pwm)
+{
+	unsigned long tcon;
+
+	/* stop timer */
+	tcon = __raw_readl(S3C2410_TCON);
+	tcon &= 0xffffff00;
+	__raw_writel(tcon, S3C2410_TCON);
+
+	clk_disable(pwm->pclk);
+	clk_put(pwm->pclk);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(s3c2410_pwm_disable);
+
+int s3c2410_pwm_init(struct s3c2410_pwm *pwm)
+{
+	pwm->pclk = clk_get(NULL, "timers");
+	if (IS_ERR(pwm->pclk))
+		return PTR_ERR(pwm->pclk);
+
+	clk_enable(pwm->pclk);
+	pwm->pclk_rate = clk_get_rate(pwm->pclk);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(s3c2410_pwm_init);
+
+int s3c2410_pwm_enable(struct s3c2410_pwm *pwm)
+{
+	unsigned long tcfg0, tcfg1, tcnt, tcmp;
+
+	/* control registers bits */
+	tcfg1 = __raw_readl(S3C2410_TCFG1);
+	tcfg0 = __raw_readl(S3C2410_TCFG0);
+
+	/* divider & scaler slection */
+	switch (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:
+		/* timer four is not capable of doing PWM */
+		break;
+	default:
+		clk_disable(pwm->pclk);
+		clk_put(pwm->pclk);
+		return -1;
+	}
+
+	/* divider & scaler values */
+	tcfg1 |= pwm->divider;
+	__raw_writel(tcfg1, S3C2410_TCFG1);
+
+	switch (pwm->timerid) {
+	case PWM0:
+	case PWM1:
+		tcfg0 |= pwm->prescaler;
+		__raw_writel(tcfg0, S3C2410_TCFG0);
+		break;
+	default:
+		if ((tcfg0 | pwm->prescaler) != tcfg0) {
+			printk(KERN_WARNING "not changing prescaler of PWM %u,"
+			       " since it's shared with timer4 (clock tick)\n",
+			       pwm->timerid);
+		}
+		break;
+	}
+
+	/* timer count and compare buffer initial values */
+	tcnt = pwm->counter;
+	tcmp = pwm->comparer;
+
+	__raw_writel(tcnt, S3C2410_TCNTB(pwm->timerid));
+	__raw_writel(tcmp, S3C2410_TCMPB(pwm->timerid));
+
+	/* ensure timer is stopped */
+	s3c2410_pwm_stop(pwm);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(s3c2410_pwm_enable);
+
+int s3c2410_pwm_start(struct s3c2410_pwm *pwm)
+{
+	unsigned long tcon;
+
+	tcon = __raw_readl(S3C2410_TCON);
+
+	switch (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:
+		/* timer four is not capable of doing PWM */
+	default:
+		return -ENODEV;
+	}
+
+	__raw_writel(tcon, S3C2410_TCON);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(s3c2410_pwm_start);
+
+int s3c2410_pwm_stop(struct s3c2410_pwm *pwm)
+{
+	unsigned long tcon;
+
+	tcon = __raw_readl(S3C2410_TCON);
+
+	switch (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:
+		/* timer four is not capable of doing PWM */
+	default:
+		return -ENODEV;
+	}
+
+	__raw_writel(tcon, S3C2410_TCON);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(s3c2410_pwm_stop);
+
+int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *pwm)
+{
+	__raw_writel(reg_value, S3C2410_TCMPB(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);
diff --git a/include/asm-arm/arch-s3c2410/pwm.h b/include/asm-arm/arch-s3c2410/pwm.h
new file mode 100644
index 0000000..a797ec3
--- /dev/null
+++ b/include/asm-arm/arch-s3c2410/pwm.h
@@ -0,0 +1,40 @@
+#ifndef __S3C2410_PWM_H
+#define __S3C2410_PWM_H
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <asm-arm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/plat-s3c/regs-timer.h>
+#include <asm/arch/gta01.h>
+
+enum pwm_timer {
+	PWM0,
+	PWM1,
+	PWM2,
+	PWM3,
+	PWM4
+};
+
+struct s3c2410_pwm {
+	enum pwm_timer timerid;
+	struct clk *pclk;
+	unsigned long pclk_rate;
+	unsigned long prescaler;
+	unsigned long divider;
+	unsigned long counter;
+	unsigned long comparer;
+};
+
+int s3c2410_pwm_init(struct s3c2410_pwm *s3c2410_pwm);
+int s3c2410_pwm_enable(struct s3c2410_pwm *s3c2410_pwm);
+int s3c2410_pwm_disable(struct s3c2410_pwm *s3c2410_pwm);
+int s3c2410_pwm_start(struct s3c2410_pwm *s3c2410_pwm);
+int s3c2410_pwm_stop(struct s3c2410_pwm *s3c2410_pwm);
+int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *s3c2410_pwm);
+int s3c2410_pwm_dumpregs(void);
+
+#endif /* __S3C2410_PWM_H */
-- 
1.5.6.5