Patchwork [U-Boot,04/14] S3C64XX: add pwm for s3c64xx support

login
register
mail settings
Submitter seedshope
Date Aug. 15, 2012, 1:48 p.m.
Message ID <1345038495-4020-5-git-send-email-bocui107@gmail.com>
Download mbox | patch
Permalink /patch/177650/
State Changes Requested
Delegated to: Minkyu Kang
Headers show

Comments

seedshope - Aug. 15, 2012, 1:48 p.m.
From: Zhong Hongbo <bocui107@gmail.com>

Signed-off-by: Zhong Hongbo <bocui107@gmail.com>
---
Change for V4:
	- None.
Change for V3:
	- None.
Change for V2:
	- Change the type of the return value from unsinged int
	to unsinged long for s3c64xx_get_base_nand function.
---
 arch/arm/cpu/arm1176/s3c64xx/Makefile       |    1 +
 arch/arm/cpu/arm1176/s3c64xx/pwm.c          |  188 +++++++++++++++++++++++++++
 arch/arm/include/asm/arch-s3c64xx/pwm.h     |   70 ++++++++++
 arch/arm/include/asm/arch-s3c64xx/s3c6400.h |   56 ++-------
 arch/arm/include/asm/arch-s3c64xx/s3c64x0.h |   59 ---------
 include/configs/smdk6400.h                  |    3 +
 6 files changed, 271 insertions(+), 106 deletions(-)
 create mode 100644 arch/arm/cpu/arm1176/s3c64xx/pwm.c
 create mode 100644 arch/arm/include/asm/arch-s3c64xx/pwm.h
 delete mode 100644 arch/arm/include/asm/arch-s3c64xx/s3c64x0.h

Patch

diff --git a/arch/arm/cpu/arm1176/s3c64xx/Makefile b/arch/arm/cpu/arm1176/s3c64xx/Makefile
index 0785b19..966663f 100644
--- a/arch/arm/cpu/arm1176/s3c64xx/Makefile
+++ b/arch/arm/cpu/arm1176/s3c64xx/Makefile
@@ -32,6 +32,7 @@  SOBJS	= reset.o
 
 COBJS-$(CONFIG_S3C6400)	+= cpu_init.o speed.o
 COBJS-y	+= timer.o
+COBJS-$(CONFIG_PWM) += pwm.o
 
 OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS-y))
 
diff --git a/arch/arm/cpu/arm1176/s3c64xx/pwm.c b/arch/arm/cpu/arm1176/s3c64xx/pwm.c
new file mode 100644
index 0000000..fff2c68
--- /dev/null
+++ b/arch/arm/cpu/arm1176/s3c64xx/pwm.c
@@ -0,0 +1,188 @@ 
+/*
+ * Copyright (C) 2012
+ * Zhong Hongbo <bocui107@gmail.com>
+ *
+ * based on arch/arm/cpu/armv7/s5p-common/pwm.c
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 <common.h>
+#include <errno.h>
+#include <pwm.h>
+#include <asm/io.h>
+#include <asm/arch/s3c6400.h>
+#include <asm/arch/pwm.h>
+
+int pwm_enable(int pwm_id)
+{
+	const struct s3c_timer *pwm =
+			(struct s3c_timer *)s3c64xx_get_base_timer();
+	unsigned long tcon;
+
+	tcon = readl(&pwm->tcon);
+	tcon |= TCON_START(pwm_id);
+
+	writel(tcon, &pwm->tcon);
+
+	return 0;
+}
+
+void pwm_disable(int pwm_id)
+{
+	const struct s3c_timer *pwm =
+			(struct s3c_timer *)s3c64xx_get_base_timer();
+	unsigned long tcon;
+
+	tcon = readl(&pwm->tcon);
+	tcon &= ~TCON_START(pwm_id);
+
+	writel(tcon, &pwm->tcon);
+}
+
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq)
+{
+	unsigned long tin_parent_rate;
+	unsigned int div;
+
+	tin_parent_rate = get_PCLK();
+
+	for (div = 2; div <= 16; div *= 2) {
+		if ((tin_parent_rate / (div << 16)) < freq)
+			return tin_parent_rate / div;
+	}
+
+	return tin_parent_rate / 16;
+}
+
+#define NS_IN_HZ (1000000000UL)
+
+int pwm_config(int pwm_id, int duty_ns, int period_ns)
+{
+	const struct s3c_timer *pwm =
+			(struct s3c_timer *)s3c64xx_get_base_timer();
+	unsigned int offset;
+	unsigned long tin_rate;
+	unsigned long tin_ns;
+	unsigned long period;
+	unsigned long tcon;
+	unsigned long tcnt;
+	unsigned long tcmp;
+
+	/*
+	 * We currently avoid using 64bit arithmetic by using the
+	 * fact that anything faster than 1GHz is easily representable
+	 * by 32bits.
+	 */
+	if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
+		return -ERANGE;
+
+	if (duty_ns > period_ns)
+		return -EINVAL;
+
+	period = NS_IN_HZ / period_ns;
+
+	/* Check to see if we are changing the clock rate of the PWM */
+	tin_rate = pwm_calc_tin(pwm_id, period);
+
+	tin_ns = NS_IN_HZ / tin_rate;
+	tcnt = period_ns / tin_ns;
+
+	/* Note, counters count down */
+	tcmp = duty_ns / tin_ns;
+	tcmp = tcnt - tcmp;
+
+	/*
+	 * the pwm hw only checks the compare register after a decrement,
+	 * so the pin never toggles if tcmp = tcnt
+	 */
+	if (tcmp == tcnt)
+		tcmp--;
+
+	if (tcmp < 0)
+		tcmp = 0;
+
+	/* Update the PWM register block. */
+	offset = pwm_id * 3;
+	if (pwm_id < 4) {
+		writel(tcnt, &pwm->tcntb0 + offset);
+		writel(tcmp, &pwm->tcmpb0 + offset);
+	}
+
+	tcon = readl(&pwm->tcon);
+	tcon |= TCON_UPDATE(pwm_id);
+	if (pwm_id < 4)
+		tcon |= TCON_AUTO_RELOAD(pwm_id);
+	else
+		tcon |= TCON4_AUTO_RELOAD;
+	writel(tcon, &pwm->tcon);
+
+	tcon &= ~TCON_UPDATE(pwm_id);
+	writel(tcon, &pwm->tcon);
+
+	return 0;
+}
+
+int pwm_init(int pwm_id, int div, int invert)
+{
+	u32 val;
+	const struct s3c_timer *pwm =
+			(struct s3c_timer *)s3c64xx_get_base_timer();
+	unsigned long timer_rate_hz;
+	unsigned int offset, prescaler;
+
+	/*
+	 * Timer Freq(HZ) =
+	 *	PWM_CLK / { (prescaler_value + 1) * (divider_value) }
+	 */
+
+	val = readl(&pwm->tcfg0);
+	if (pwm_id < 2) {
+		prescaler = PRESCALER_0;
+		val &= ~0xff;
+		val |= (prescaler & 0xff);
+	} else {
+		prescaler = PRESCALER_1;
+		val &= ~(0xff << 8);
+		val |= (prescaler & 0xff) << 8;
+	}
+	writel(val, &pwm->tcfg0);
+	val = readl(&pwm->tcfg1);
+	val &= ~(0xf << MUX_DIV_SHIFT(pwm_id));
+	val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id);
+	writel(val, &pwm->tcfg1);
+
+	timer_rate_hz = get_PCLK() / ((prescaler + 1) *
+			(div + 1));
+
+	timer_rate_hz = timer_rate_hz / CONFIG_SYS_HZ;
+
+	/* set count value */
+	offset = pwm_id * 3;
+	writel(timer_rate_hz, &pwm->tcntb0 + offset);
+
+	val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id));
+	if (invert && (pwm_id < 4))
+		val |= TCON_INVERTER(pwm_id);
+	writel(val, &pwm->tcon);
+
+	pwm_enable(pwm_id);
+
+	return 0;
+}
diff --git a/arch/arm/include/asm/arch-s3c64xx/pwm.h b/arch/arm/include/asm/arch-s3c64xx/pwm.h
new file mode 100644
index 0000000..d407dc8
--- /dev/null
+++ b/arch/arm/include/asm/arch-s3c64xx/pwm.h
@@ -0,0 +1,70 @@ 
+/*
+ * Copyright (C) 2012
+ * Zhong Hongbo <bocui107@gmail.com>
+ *
+ * based on arch/arm/include/asm/arch-s5pc1xx/pwm.h
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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
+ */
+
+#ifndef __ASM_ARM_ARCH_PWM_H__
+#define __ASM_ARM_ARCH_PWM_H__
+
+#define PRESCALER_0		(8 - 1)		/* prescaler of timer 0, 1 */
+#define PRESCALER_1		(16 - 1)	/* prescaler of timer 2, 3, 4 */
+
+/* Divider MUX */
+#define MUX_DIV_1		0		/* 1/1 period */
+#define MUX_DIV_2		1		/* 1/2 period */
+#define MUX_DIV_4		2		/* 1/4 period */
+#define MUX_DIV_8		3		/* 1/8 period */
+#define MUX_DIV_16		4		/* 1/16 period */
+
+#define MUX_DIV_SHIFT(x)	(x * 4)
+
+#define TCON_OFFSET(x)		((x + 1) * (!!x) << 2)
+
+#define TCON_START(x)		(1 << TCON_OFFSET(x))
+#define TCON_UPDATE(x)		(1 << (TCON_OFFSET(x) + 1))
+#define TCON_INVERTER(x)	(1 << (TCON_OFFSET(x) + 2))
+#define TCON_AUTO_RELOAD(x)	(1 << (TCON_OFFSET(x) + 3))
+#define TCON4_AUTO_RELOAD	(1 << 22)
+
+#define TCFG1_DMA(x)		(x << 20)
+
+#ifndef __ASSEMBLY__
+struct s3c_timer {
+	unsigned int	tcfg0;
+	unsigned int	tcfg1;
+	unsigned int	tcon;
+	unsigned int	tcntb0;
+	unsigned int	tcmpb0;
+	unsigned int	tcnto0;
+	unsigned int	tcntb1;
+	unsigned int	tcmpb1;
+	unsigned int	tcnto1;
+	unsigned int	tcntb2;
+	unsigned int	res1;
+	unsigned int	tcnto2;
+	unsigned int	tcntb3;
+	unsigned int	res2;
+	unsigned int	tcnto3;
+	unsigned int	tcntb4;
+	unsigned int	tcnto4;
+	unsigned int	tint_cstat;
+};
+#endif
+#endif
diff --git a/arch/arm/include/asm/arch-s3c64xx/s3c6400.h b/arch/arm/include/asm/arch-s3c64xx/s3c6400.h
index 77b9509..b884763 100644
--- a/arch/arm/include/asm/arch-s3c64xx/s3c6400.h
+++ b/arch/arm/include/asm/arch-s3c64xx/s3c6400.h
@@ -31,6 +31,10 @@ 
 #ifndef __S3C6400_H__
 #define __S3C6400_H__
 
+#if defined(CONFIG_SYNC_MODE) && defined(CONFIG_S3C6400)
+#error CONFIG_SYNC_MODE unavailable on S3C6400, please, fix your configuration!
+#endif
+
 #define S3C64XX_UART_CHANNELS	3
 #define S3C64XX_SPI_CHANNELS	2
 
@@ -587,51 +591,6 @@ 
  */
 #define ELFIN_TIMER_BASE	0x7F006000
 
-#define TCFG0_REG	__REG(0x7F006000)
-#define TCFG1_REG	__REG(0x7F006004)
-#define TCON_REG	__REG(0x7F006008)
-#define TCNTB0_REG	__REG(0x7F00600c)
-#define TCMPB0_REG	__REG(0x7F006010)
-#define TCNTO0_REG	__REG(0x7F006014)
-#define TCNTB1_REG	__REG(0x7F006018)
-#define TCMPB1_REG	__REG(0x7F00601c)
-#define TCNTO1_REG	__REG(0x7F006020)
-#define TCNTB2_REG	__REG(0x7F006024)
-#define TCMPB2_REG	__REG(0x7F006028)
-#define TCNTO2_REG	__REG(0x7F00602c)
-#define TCNTB3_REG	__REG(0x7F006030)
-#define TCMPB3_REG	__REG(0x7F006034)
-#define TCNTO3_REG	__REG(0x7F006038)
-#define TCNTB4_REG	__REG(0x7F00603c)
-#define TCNTO4_REG	__REG(0x7F006040)
-
-/* Fields */
-#define fTCFG0_DZONE		Fld(8, 16) /* the dead zone length (=timer 0) */
-#define fTCFG0_PRE1		Fld(8, 8)  /* prescaler value for time 2,3,4 */
-#define fTCFG0_PRE0		Fld(8, 0)  /* prescaler value for time 0,1 */
-#define fTCFG1_MUX4		Fld(4, 16)
-/* bits */
-#define TCFG0_DZONE(x)		FInsrt((x), fTCFG0_DZONE)
-#define TCFG0_PRE1(x)		FInsrt((x), fTCFG0_PRE1)
-#define TCFG0_PRE0(x)		FInsrt((x), fTCFG0_PRE0)
-#define TCON_4_AUTO		(1 << 22)  /* auto reload on/off for Timer 4 */
-#define TCON_4_UPDATE		(1 << 21)  /* manual Update TCNTB4 */
-#define TCON_4_ONOFF		(1 << 20)  /* 0: Stop, 1: start Timer 4 */
-#define COUNT_4_ON		(TCON_4_ONOFF * 1)
-#define COUNT_4_OFF		(TCON_4_ONOFF * 0)
-#define TCON_3_AUTO		(1 << 19)  /* auto reload on/off for Timer 3 */
-#define TIMER3_ATLOAD_ON	(TCON_3_AUTO * 1)
-#define TIMER3_ATLAOD_OFF	FClrBit(TCON, TCON_3_AUTO)
-#define TCON_3_INVERT		(1 << 18)  /* 1: Inverter on for TOUT3 */
-#define TIMER3_IVT_ON		(TCON_3_INVERT * 1)
-#define TIMER3_IVT_OFF		(FClrBit(TCON, TCON_3_INVERT))
-#define TCON_3_MAN		(1 << 17)  /* manual Update TCNTB3,TCMPB3 */
-#define TIMER3_MANUP		(TCON_3_MAN*1)
-#define TIMER3_NOP		(FClrBit(TCON, TCON_3_MAN))
-#define TCON_3_ONOFF		(1 << 16)  /* 0: Stop, 1: start Timer 3 */
-#define TIMER3_ON		(TCON_3_ONOFF * 1)
-#define TIMER3_OFF		(FClrBit(TCON, TCON_3_ONOFF))
-
 #if defined(CONFIG_CLK_400_100_50)
 #define STARTUP_AMDIV		400
 #define STARTUP_MDIV		400
@@ -749,8 +708,6 @@ 
 
 #ifndef __ASSEMBLY__
 
-#include "s3c64x0.h"
-
 static inline unsigned long s3c64xx_get_base_uart(void)
 {
 	return ELFIN_UART_BASE;
@@ -760,6 +717,11 @@  static inline unsigned long s3c64xx_get_base_nand(void)
 {
 	return ELFIN_NAND_BASE;
 }
+
+static inline unsigned long s3c64xx_get_base_timer(void)
+{
+	return ELFIN_TIMER_BASE;
+}
 #endif
 
 #endif /*__S3C6400_H__*/
diff --git a/arch/arm/include/asm/arch-s3c64xx/s3c64x0.h b/arch/arm/include/asm/arch-s3c64xx/s3c64x0.h
deleted file mode 100644
index 7add68c..0000000
--- a/arch/arm/include/asm/arch-s3c64xx/s3c64x0.h
+++ /dev/null
@@ -1,59 +0,0 @@ 
-/*
- * (C) Copyright 2003
- * David MÃŒller ELSOFT AG Switzerland. d.mueller@elsoft.ch
- *
- * (C) Copyright 2008
- * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * 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
- */
-
-/************************************************
- * NAME	    : S3C64XX.h
- * Version  : 31.3.2003
- *
- * common stuff for SAMSUNG S3C64XX SoC
- ************************************************/
-
-#ifndef __S3C64XX_H__
-#define __S3C64XX_H__
-
-#if defined(CONFIG_SYNC_MODE) && defined(CONFIG_S3C6400)
-#error CONFIG_SYNC_MODE unavailable on S3C6400, please, fix your configuration!
-#endif
-
-#include <asm/types.h>
-
-/* PWM TIMER (see manual chapter 10) */
-typedef struct {
-	volatile u32	TCNTB;
-	volatile u32	TCMPB;
-	volatile u32	TCNTO;
-} s3c64xx_timer;
-
-typedef struct {
-	volatile u32	TCFG0;
-	volatile u32	TCFG1;
-	volatile u32	TCON;
-	s3c64xx_timer	ch[4];
-	volatile u32	TCNTB4;
-	volatile u32	TCNTO4;
-} s3c64xx_timers;
-
-#endif /*__S3C64XX_H__*/
diff --git a/include/configs/smdk6400.h b/include/configs/smdk6400.h
index 47326d6..d85ddeb 100644
--- a/include/configs/smdk6400.h
+++ b/include/configs/smdk6400.h
@@ -141,6 +141,9 @@ 
 
 #define CONFIG_SYS_HZ			1000
 
+/* PWM */
+#define CONFIG_PWM			1
+
 /*-----------------------------------------------------------------------
  * Stack sizes
  *