[PATCH] [ARM] [S3C64XX] Add support for hr timer

aditya aditya.ps at samsung.com
Tue Dec 1 03:39:55 EST 2009


This patch add support for the S3C64XX hr timer.

Signed-off-by: Aditya Pratap Sharma <aditya.ps at samsung.com>
---
 arch/arm/mach-s3c6410/Kconfig               |    2 +
 arch/arm/plat-s3c/Makefile                  |   10 +
 arch/arm/plat-s3c/hr-time.c                 |  301 +++++++++++++++++++++++++++
 arch/arm/plat-s3c/include/plat/regs-timer.h |   11 +
 4 files changed, 324 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/plat-s3c/hr-time.c

diff --git a/arch/arm/mach-s3c6410/Kconfig b/arch/arm/mach-s3c6410/Kconfig
index f9d0f09..29cfd0c 100644
--- a/arch/arm/mach-s3c6410/Kconfig
+++ b/arch/arm/mach-s3c6410/Kconfig
@@ -11,6 +11,8 @@ config CPU_S3C6410
 	bool
 	select CPU_S3C6400_INIT
 	select CPU_S3C6400_CLOCK
+	select GENERIC_TIME
+	select GENERIC_CLOCKEVENTS
 	help
 	  Enable S3C6410 CPU support
 
diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile
index 3c09109..6c612aa 100644
--- a/arch/arm/plat-s3c/Makefile
+++ b/arch/arm/plat-s3c/Makefile
@@ -12,7 +12,17 @@ obj-				:=
 # Core support for all Samsung SoCs
 
 obj-y				+=  init.o
+ifdef CONFIG_NO_HZ
+obj-y				+= hr-time.o
+else
+
+ifndef CONFIG_HIGH_RES_TIMERS
 obj-y				+= time.o
+else
+obj-y				+= hr-time.o
+endif
+
+endif
 obj-y				+= clock.o
 obj-y				+= pwm-clock.o
 obj-y				+= gpio.o
diff --git a/arch/arm/plat-s3c/hr-time.c b/arch/arm/plat-s3c/hr-time.c
new file mode 100644
index 0000000..76fd91f
--- /dev/null
+++ b/arch/arm/plat-s3c/hr-time.c
@@ -0,0 +1,301 @@
+/*
+ * linux/arch/arm/plat-s3c/hr-time.c
+ *
+ * S3C6410 high resolution Timers
+ *
+ * Copyright (c) 2006 Samsung Electronics
+ *
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/map.h>
+#include <mach/regs-irq.h>
+#include <mach/tick.h>
+#include <plat/regs-timer.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+
+static void s3c64xx_timer_setup(void);
+
+static inline void s3c64xx_tick_set_autoreset(void)
+{
+	unsigned long tcon;
+	tcon  = __raw_readl(S3C2410_TCON);
+	tcon |= (S3C2410_TCON_T4RELOAD);
+	__raw_writel(tcon, S3C2410_TCON);
+}
+
+static inline void s3c64xx_tick_remove_autoreset(void)
+{
+	unsigned long tcon;
+	tcon  = __raw_readl(S3C2410_TCON);
+	tcon &= ~(S3C2410_TCON_T4RELOAD);
+	__raw_writel(tcon, S3C2410_TCON);
+}
+
+static void s3c64xx_tick_timer_start(unsigned long load_val,
+					int autoreset)
+{
+	unsigned long tcon;
+	unsigned long tcfg1;
+	unsigned long tcfg0;
+	unsigned long tcstat;
+
+	tcon  = __raw_readl(S3C2410_TCON);
+	tcfg1 = __raw_readl(S3C2410_TCFG1);
+	tcfg0 = __raw_readl(S3C2410_TCFG0);
+
+	tcstat = __raw_readl(S3C64XX_TINT_CSTAT);
+	tcstat |=  S3C_TINT_CSTAT_T4INTEN;
+	__raw_writel(tcstat, S3C64XX_TINT_CSTAT);
+	__raw_writel(load_val - 1, S3C2410_TCNTB(4));
+
+	tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
+	tcfg1 |= S3C2410_TCFG1_MUX4_DIV2;
+
+	tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
+	tcfg0 |= (0) << S3C2410_TCFG_PRESCALER1_SHIFT;
+
+	__raw_writel(tcfg1, S3C2410_TCFG1);
+	__raw_writel(tcfg0, S3C2410_TCFG0);
+
+	tcon &= ~(S3C2410_TCON_T4START|S3C2410_TCON_T4MANUALUPD
+					|S3C2410_TCON_T4RELOAD);
+	tcon |= S3C2410_TCON_T4MANUALUPD;
+	if (autoreset)
+		tcon |= S3C2410_TCON_T4RELOAD;
+	__raw_writel(tcon, S3C2410_TCON);
+
+	/* start the timer running */
+	tcon |= S3C2410_TCON_T4START;
+	tcon &= ~S3C2410_TCON_T4MANUALUPD;
+	__raw_writel(tcon, S3C2410_TCON);
+}
+
+static inline void s3c64xx_tick_timer_stop(void)
+{
+	unsigned long tcon;
+	tcon  = __raw_readl(S3C2410_TCON);
+	tcon &= ~(S3C2410_TCON_T4START);
+	__raw_writel(tcon, S3C2410_TCON);
+}
+
+static void s3c64xx_sched_timer_start(unsigned long load_val,
+					int autoreset)
+{
+	unsigned long tcon;
+	unsigned long tcfg1;
+	unsigned long tcfg0;
+	unsigned long tcstat;
+
+	tcstat = __raw_readl(S3C64XX_TINT_CSTAT);
+	tcstat |=  S3C_TINT_CSTAT_T2INTEN;
+	__raw_writel(tcstat, S3C64XX_TINT_CSTAT);
+
+	tcon  = __raw_readl(S3C2410_TCON);
+	tcfg1 = __raw_readl(S3C2410_TCFG1);
+	tcfg0 = __raw_readl(S3C2410_TCFG0);
+
+	__raw_writel(load_val - 1, S3C2410_TCNTB(2));
+	__raw_writel(load_val - 1, S3C2410_TCMPB(2));
+
+	tcon &= ~(S3C2410_TCON_T2RELOAD|(!S3C2410_TCON_T2INVERT)
+			|S3C2410_TCON_T2MANUALUPD|S3C2410_TCON_T2START);
+	if (autoreset)
+		tcon |= S3C2410_TCON_T2RELOAD;
+	tcon |= S3C2410_TCON_T2MANUALUPD;
+	__raw_writel(tcon, S3C2410_TCON);
+
+	/* start the timer running */
+	tcon |= S3C2410_TCON_T2START;
+	tcon &= ~S3C2410_TCON_T2MANUALUPD;
+	__raw_writel(tcon, S3C2410_TCON);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * PWM timer 4 ... count down to zero, interrupt, reload
+ * ---------------------------------------------------------------------------
+ */
+static int s3c64xx_tick_set_next_event(unsigned long cycles,
+				   struct clock_event_device *evt)
+{
+	s3c64xx_tick_timer_start(cycles, 0);
+	return 0;
+}
+static void s3c64xx_tick_set_mode(enum clock_event_mode mode,
+			      struct clock_event_device *evt)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		s3c64xx_tick_set_autoreset();
+		break;
+	case CLOCK_EVT_MODE_ONESHOT:
+		s3c64xx_tick_timer_stop();
+		s3c64xx_tick_remove_autoreset();
+		break;
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		break;
+	case CLOCK_EVT_MODE_RESUME:
+		s3c64xx_timer_setup();
+		break;
+	}
+}
+
+static struct clock_event_device tick_tmr = {
+	.name		= "pwm_timer4",
+	.features	= CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+	.shift		= 32,
+	.set_next_event	= s3c64xx_tick_set_next_event,
+	.set_mode	= s3c64xx_tick_set_mode,
+};
+
+irqreturn_t s3c64xx_tick_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = &tick_tmr;
+	evt->event_handler(evt);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction s3c64xx_tick_timer_irq = {
+	.name		= "pwm_timer4",
+	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+	.handler	= s3c64xx_tick_timer_interrupt,
+};
+
+static void __init s3c64xx_init_dynamic_tick_timer(unsigned long rate)
+{
+	s3c64xx_tick_timer_start((rate / HZ) - 1, 1);
+	tick_tmr.mult = div_sc(rate, NSEC_PER_SEC, tick_tmr.shift);
+	tick_tmr.max_delta_ns = clockevent_delta2ns(-1, &tick_tmr);
+	tick_tmr.min_delta_ns = clockevent_delta2ns(1, &tick_tmr);
+
+	tick_tmr.cpumask = get_cpu_mask(0);
+	clockevents_register_device(&tick_tmr);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * PWM timer 2 ... free running 32-bit clock source and scheduler clock
+ * ---------------------------------------------------------------------------
+ */
+static unsigned long s3c64xx_mpu_timer2_overflows;
+
+irqreturn_t s3c64xx_mpu_timer2_interrupt(int irq, void *dev_id)
+{
+	s3c64xx_mpu_timer2_overflows++;
+	return IRQ_HANDLED;
+}
+
+struct irqaction s3c64xx_timer2_irq = {
+	.name		= "pwm_timer2",
+	.flags		= IRQF_DISABLED ,
+	.handler	= s3c64xx_mpu_timer2_interrupt,
+};
+
+
+/* Read timer 2 Count Observation Register */
+static cycle_t s3c64xx_sched_timer_read(struct clocksource *cs)
+{
+	return (cycle_t)~__raw_readl(S3C_TIMERREG(0x2c));
+}
+
+struct clocksource clocksource_s3c64xx = {
+	.name		= "clock_source_timer2",
+	.rating		= 300,
+	.read		= s3c64xx_sched_timer_read,
+	.mask		= CLOCKSOURCE_MASK(32),
+	.shift		= 20,
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS ,
+};
+
+static void __init s3c64xx_init_clocksource(unsigned long rate)
+{
+	clocksource_s3c64xx.mult
+		= clocksource_khz2mult(rate/1000, clocksource_s3c64xx.shift);
+	s3c64xx_sched_timer_start(~0, 1);
+
+	if (clocksource_register(&clocksource_s3c64xx))
+		printk(KERN_ERR "%s: can't register clocksource\n",
+			clocksource_s3c64xx.name);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *  Tick Timer initialization
+ * ---------------------------------------------------------------------------
+ */
+static void s3c64xx_dynamic_timer_setup(void)
+{
+	struct clk	*ck_ref = clk_get(NULL, "timers");
+	unsigned long	rate;
+
+	if (IS_ERR(ck_ref))
+		panic("failed to get clock for system timer");
+
+	rate = clk_get_rate(ck_ref);
+	clk_put(ck_ref);
+
+	s3c64xx_init_dynamic_tick_timer(rate);
+	s3c64xx_init_clocksource(rate);
+}
+
+static void s3c64xx_timer_setup(void)
+{
+	struct clk	*ck_ref = clk_get(NULL, "timers");
+	unsigned long	rate;
+
+	if (IS_ERR(ck_ref))
+		panic("failed to get clock for system timer");
+
+	rate = clk_get_rate(ck_ref);
+	clk_put(ck_ref);
+	s3c64xx_tick_timer_start((rate / HZ) - 1, 1);
+	s3c64xx_sched_timer_start(~0, 1);
+}
+
+
+static void __init s3c64xx_dynamic_timer_init(void)
+{
+	s3c64xx_dynamic_timer_setup();
+	setup_irq(IRQ_TIMER2, &s3c64xx_timer2_irq);
+	setup_irq(IRQ_TIMER4, &s3c64xx_tick_timer_irq);
+}
+
+
+struct sys_timer s3c24xx_timer = {
+	.init		= s3c64xx_dynamic_timer_init,
+};
+
diff --git a/arch/arm/plat-s3c/include/plat/regs-timer.h b/arch/arm/plat-s3c/include/plat/regs-timer.h
index d097d92..e9994db 100644
--- a/arch/arm/plat-s3c/include/plat/regs-timer.h
+++ b/arch/arm/plat-s3c/include/plat/regs-timer.h
@@ -117,6 +117,17 @@
 #define S3C2410_TCON_T0INVERT	  (1<<2)
 #define S3C2410_TCON_T0MANUALUPD  (1<<1)
 #define S3C2410_TCON_T0START	  (1<<0)
+/* Interrupt Control and Status register*/
+#define S3C_TINT_CSTAT_T4INT	(1<<9)
+#define S3C_TINT_CSTAT_T3INT	(1<<8)
+#define S3C_TINT_CSTAT_T2INT	(1<<7)
+#define S3C_TINT_CSTAT_T1INT	(1<<6)
+#define S3C_TINT_CSTAT_T0INT	(1<<5)
+#define S3C_TINT_CSTAT_T4INTEN	(1<<4)
+#define S3C_TINT_CSTAT_T3INTEN	(1<<3)
+#define S3C_TINT_CSTAT_T2INTEN	(1<<2)
+#define S3C_TINT_CSTAT_T1INTEN	(1<<1)
+#define S3C_TINT_CSTAT_T0INTEN	(1<<0)
 
 #endif /*  __ASM_ARCH_REGS_TIMER_H */
 
-- 
1.6.2.5




More information about the linux-arm-kernel mailing list