[U-Boot] [V2 04/15] S3C64XX: add pwm for s3c64xx support

Zhong Hongbo bocui107 at gmail.com
Fri Jul 20 00:04:56 CEST 2012


Hi Minkyu,

I notice this patch are delegated to you. But the patchwork miss the
patch 4.

If you need me to do anything, Please let me know.

Thanks,
hongbo

On 07/14/2012 12:11 AM, Zhong Hongbo wrote:
> From: Zhong Hongbo <bocui107 at gmail.com>
> 
> Signed-off-by: Zhong Hongbo <bocui107 at gmail.com>
> ---
> 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          |  189 +++++++++++++++++++++++++++
>  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, 272 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
> 
> 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..d1d70ff
> --- /dev/null
> +++ b/arch/arm/cpu/arm1176/s3c64xx/pwm.c
> @@ -0,0 +1,189 @@
> +/*
> + * Copyright (C) 2012
> + *
> + * Zhong Hongbo <bocui107 at gmail.com>
> + *
> + * based on arch/arm/cpu/armv7/s5p-common/sromc.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..1e18f8c
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-s3c64xx/pwm.h
> @@ -0,0 +1,70 @@
> +/*
> + * Copyright (C) 2012
> + * Zhong Hongbo <bocui107 at 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 at elsoft.ch
> - *
> - * (C) Copyright 2008
> - * Guennadi Liakhovetki, DENX Software Engineering, <lg at 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..3642a5c 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
>   *
> 




More information about the U-Boot mailing list