From: Jongpill Lee <[email protected]>

This patch addes system timer for Samsung S5P series SoCs

Signed-off-by: Jongpill Lee <[email protected]>
Signed-off-by: Kukjin Kim <[email protected]>
---
 arch/arm/mach-s5p6442/include/mach/tick.h      |    6 +
 arch/arm/mach-s5p6442/mach-smdk6442.c          |    2 +-
 arch/arm/mach-s5pv210/include/mach/tick.h      |    6 +
 arch/arm/mach-s5pv210/mach-smdkv210.c          |    2 +-
 arch/arm/plat-s5p/Kconfig                      |    7 +
 arch/arm/plat-s5p/Makefile                     |    1 +
 arch/arm/plat-s5p/include/plat/regs-systimer.h |   75 ++++++
 arch/arm/plat-s5p/systimer-s5p.c               |  298 ++++++++++++++++++++++++
 arch/arm/plat-samsung/include/plat/cpu.h       |    3 +
 9 files changed, 398 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/plat-s5p/include/plat/regs-systimer.h
 create mode 100644 arch/arm/plat-s5p/systimer-s5p.c

diff --git a/arch/arm/mach-s5p6442/include/mach/tick.h 
b/arch/arm/mach-s5p6442/include/mach/tick.h
index e1d4cab..1795b43 100644
--- a/arch/arm/mach-s5p6442/include/mach/tick.h
+++ b/arch/arm/mach-s5p6442/include/mach/tick.h
@@ -21,6 +21,12 @@ static inline u32 s3c24xx_ostimer_pending(void)
        return pend & (1 << (IRQ_TIMER4_VIC - S5P_IRQ_VIC0(0)));
 }
 
+static inline u32 s5p_ostimer_pending(void)
+{
+       u32 pend = __raw_readl(VA_VIC0 + VIC_RAW_STATUS);
+       return pend & (1 << (IRQ_SYSTIMER - S5P_IRQ_VIC0(0)));
+}
+
 #define TICK_MAX       (0xffffffff)
 
 #endif /* __ASM_ARCH_TICK_H */
diff --git a/arch/arm/mach-s5p6442/mach-smdk6442.c 
b/arch/arm/mach-s5p6442/mach-smdk6442.c
index 0d63371..825df9d 100644
--- a/arch/arm/mach-s5p6442/mach-smdk6442.c
+++ b/arch/arm/mach-s5p6442/mach-smdk6442.c
@@ -87,5 +87,5 @@ MACHINE_START(SMDK6442, "SMDK6442")
        .init_irq       = s5p6442_init_irq,
        .map_io         = smdk6442_map_io,
        .init_machine   = smdk6442_machine_init,
-       .timer          = &s3c24xx_timer,
+       .timer          = &s5p_systimer,
 MACHINE_END
diff --git a/arch/arm/mach-s5pv210/include/mach/tick.h 
b/arch/arm/mach-s5pv210/include/mach/tick.h
index 7993b36..9fc5a8d 100644
--- a/arch/arm/mach-s5pv210/include/mach/tick.h
+++ b/arch/arm/mach-s5pv210/include/mach/tick.h
@@ -21,6 +21,12 @@ static inline u32 s3c24xx_ostimer_pending(void)
        return pend & (1 << (IRQ_TIMER4_VIC - S5P_IRQ_VIC0(0)));
 }
 
+static inline u32 s5p_ostimer_pending(void)
+{
+       u32 pend = __raw_readl(VA_VIC0 + VIC_RAW_STATUS);
+       return pend & (1 << (IRQ_SYSTIMER - S5P_IRQ_VIC0(0)));
+}
+
 #define TICK_MAX       (0xffffffff)
 
 #endif /* __ASM_ARCH_TICK_H */
diff --git a/arch/arm/mach-s5pv210/mach-smdkv210.c 
b/arch/arm/mach-s5pv210/mach-smdkv210.c
index a278832..22ed209 100644
--- a/arch/arm/mach-s5pv210/mach-smdkv210.c
+++ b/arch/arm/mach-s5pv210/mach-smdkv210.c
@@ -94,5 +94,5 @@ MACHINE_START(SMDKV210, "SMDKV210")
        .init_irq       = s5pv210_init_irq,
        .map_io         = smdkv210_map_io,
        .init_machine   = smdkv210_machine_init,
-       .timer          = &s3c24xx_timer,
+       .timer          = &s5p_systimer,
 MACHINE_END
diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig
index d400a6a..a73fc56 100644
--- a/arch/arm/plat-s5p/Kconfig
+++ b/arch/arm/plat-s5p/Kconfig
@@ -23,3 +23,10 @@ config PLAT_S5P
        select SAMSUNG_IRQ_UART
        help
          Base platform code for Samsung's S5P series SoC.
+
+config SYSTIMER_S5P
+       bool
+       depends on (ARCH_S5P6442 || ARCH_S5PV210)
+       default y
+       help
+         Support System Timer for S5P Series
diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile
index a7c54b3..ec28f1b 100644
--- a/arch/arm/plat-s5p/Makefile
+++ b/arch/arm/plat-s5p/Makefile
@@ -17,3 +17,4 @@ obj-y                         += cpu.o
 obj-y                          += clock.o
 obj-y                          += irq.o
 obj-y                          += setup-i2c0.o
+obj-$(CONFIG_SYSTIMER_S5P)     += systimer-s5p.o
diff --git a/arch/arm/plat-s5p/include/plat/regs-systimer.h 
b/arch/arm/plat-s5p/include/plat/regs-systimer.h
new file mode 100644
index 0000000..937ec44
--- /dev/null
+++ b/arch/arm/plat-s5p/include/plat/regs-systimer.h
@@ -0,0 +1,75 @@
+/* linux/arch/arm/plat-s5p/include/plat/regs-systimer.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com/
+ *
+ * S5P System Timer Driver Header information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_REGS_SYSTIMER_H
+#define __ASM_PLAT_REGS_SYSTIMER_H __FILE__
+
+#define S5P_SYSTIMERREG(x)             (S5P_VA_SYSTIMER + (x))
+
+#define S5P_SYSTIMER_TCFG              S5P_SYSTIMERREG(0x00)
+#define S5P_SYSTIMER_TCON              S5P_SYSTIMERREG(0x04)
+#define S5P_SYSTIMER_TICNTB            S5P_SYSTIMERREG(0x08)
+#define S5P_SYSTIMER_TICNTO            S5P_SYSTIMERREG(0x0c)
+#define S5P_SYSTIMER_TFCNTB            S5P_SYSTIMERREG(0x10)
+#define S5P_SYSTIMER_ICNTB             S5P_SYSTIMERREG(0x18)
+#define S5P_SYSTIMER_ICNTO             S5P_SYSTIMERREG(0x1c)
+#define S5P_SYSTIMER_INT_CSTAT         S5P_SYSTIMERREG(0x20)
+
+/* Value for TCFG */
+
+#define S5P_SYSTIMER_SWRST             (1<<16)
+
+#define S5P_SYSTIMER_DIV_GEN           (0<<15)
+#define S5P_SYSTIMER_DIV_RTC           (1<<15)
+
+#define S5P_SYSTIMER_TICK_INT          (0<<14)
+#define S5P_SYSTIMER_TICK_FRA          (1<<14)
+
+#define S5P_SYSTIMER_TCLK_MASK         (3<<12)
+#define S5P_SYSTIMER_TCLK_XXTI         (0<<12)
+#define S5P_SYSTIMER_TCLK_RTC          (1<<12)
+#define S5P_SYSTIMER_TCLK_USB          (2<<12)
+#define S5P_SYSTIMER_TCLK_PCLK         (3<<12)
+
+#define S5P_SYSTIMER_DIV_MASK          (7<<8)
+#define S5P_SYSTIMER_DIV_1             (0<<8)
+#define S5P_SYSTIMER_DIV_2             (1<<8)
+#define S5P_SYSTIMER_DIV_4             (2<<8)
+#define S5P_SYSTIMER_DIV_8             (3<<8)
+#define S5P_SYSTIMER_DIV_16            (4<<8)
+
+#define S5P_SYSTIMER_TARGET_HZ         1000
+#define S5P_SYSTIMER_PRESCALER         5
+#define S5P_SYSTIMER_PRESCALER_MASK    (0x3f<<0)
+
+/* value for TCON */
+
+#define S5P_SYSTIMER_INT_AUTO          (1<<5)
+#define S5P_SYSTIMER_INT_IMM           (1<<4)
+#define S5P_SYSTIMER_INT_START         (1<<3)
+#define S5P_SYSTIMER_START             (1<<0)
+
+/* Value for INT_CSTAT */
+
+#define S5P_SYSTIMER_INT_TWIE          (1<<10)
+#define S5P_SYSTIMER_INT_IWIE          (1<<9)
+#define S5P_SYSTIMER_INT_TFWIE         (1<<8)
+#define S5P_SYSTIMER_INT_TIWIE         (1<<7)
+#define S5P_SYSTIMER_INT_ICNTEIE       (1<<6)
+#define S5P_SYSTIMER_INT_TCON          (1<<5)
+#define S5P_SYSTIMER_INT_ICNTB         (1<<4)
+#define S5P_SYSTIMER_INT_TFCNTB                (1<<3)
+#define S5P_SYSTIMER_INT_TICNTB                (1<<2)
+#define S5P_SYSTIMER_INT_INTCNT                (1<<1)
+#define S5P_SYSTIMER_INT_INTENABLE     (1<<0)
+
+#endif /* __ASM_PLAT_REGS_SYSTIMER_H */
diff --git a/arch/arm/plat-s5p/systimer-s5p.c b/arch/arm/plat-s5p/systimer-s5p.c
new file mode 100644
index 0000000..66951c8
--- /dev/null
+++ b/arch/arm/plat-s5p/systimer-s5p.c
@@ -0,0 +1,298 @@
+/* linux/arch/arm/plat-s5p/systimer-s5p.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com/
+ *
+ * S5P System Timer
+ *
+ * Based on linux/arch/arm/plat-samsung/time.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include <asm/irq.h>
+#include <asm/mach/time.h>
+
+#include <mach/map.h>
+#include <mach/regs-irq.h>
+#include <mach/tick.h>
+
+#include <plat/regs-systimer.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+#include <mach/regs-clock.h>
+
+static unsigned long timer_startval;
+static unsigned long timer_usec_ticks;
+static unsigned long timer_icnt;
+
+#define TICK_MAX               (0xffffffff)
+#define TIMER_USEC_SHIFT       16
+
+static unsigned int systimer_write_done(unsigned int value)
+{
+       unsigned int cnt;
+       unsigned int tmp_reg;
+
+       cnt = 1000;
+
+       do {
+               cnt--;
+
+               if (__raw_readl(S5P_SYSTIMER_INT_CSTAT) & value) {
+                       tmp_reg = __raw_readl(S5P_SYSTIMER_INT_CSTAT);
+                       tmp_reg |= value;
+                       __raw_writel(tmp_reg , S5P_SYSTIMER_INT_CSTAT);
+
+                       return 0;
+               }
+
+       } while (cnt > 0);
+
+       printk(KERN_ERR "%s : %d : Timer Expired\n", __func__, value);
+
+       return -ETIME;
+}
+
+static unsigned int s5p_systimer_write(void __iomem *reg_offset,
+                                      unsigned int value)
+{
+       unsigned int int_cstat;
+       unsigned int ret = 0;
+
+       int_cstat = __raw_readl(S5P_SYSTIMER_INT_CSTAT);
+
+       if (reg_offset == S5P_SYSTIMER_TCON) {
+               __raw_writel(value, reg_offset);
+
+               if (int_cstat & S5P_SYSTIMER_INT_TWIE)
+                       ret = systimer_write_done(S5P_SYSTIMER_INT_TCON);
+
+       } else if (reg_offset == S5P_SYSTIMER_ICNTB) {
+               __raw_writel(value, reg_offset);
+
+               if (int_cstat & S5P_SYSTIMER_INT_IWIE)
+                       ret = systimer_write_done(S5P_SYSTIMER_INT_ICNTB);
+
+       } else if (reg_offset == S5P_SYSTIMER_TFCNTB) {
+               __raw_writel(value, reg_offset);
+
+               if (int_cstat & S5P_SYSTIMER_INT_TFWIE)
+                       ret = systimer_write_done(S5P_SYSTIMER_INT_TFCNTB);
+
+       } else if (reg_offset == S5P_SYSTIMER_TICNTB) {
+               __raw_writel(value, reg_offset);
+
+               if (int_cstat & S5P_SYSTIMER_INT_TIWIE)
+                       ret = systimer_write_done(S5P_SYSTIMER_INT_TICNTB);
+       } else {
+               __raw_writel(value, reg_offset);
+       }
+
+       return ret;
+}
+
+/*
+ * S5P has system timer to use as OS tick Timer.
+ * System Timer provides two distincive feature. Accurate timer which provides
+ * exact 1ms time tick at any power mode except sleep mode.
+ * interrupt interval without stopping reference tick timer.
+ */
+
+/*
+ * timer_mask_usec_ticks
+ *
+ * given a clock and divisor, make the value to pass into timer_ticks_to_usec
+ * to scale the ticks into usecs
+ */
+static inline unsigned long timer_mask_usec_ticks(unsigned long scaler,
+                                                 unsigned long pclk)
+{
+       unsigned long den = pclk / 1000;
+
+       return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den;
+}
+
+/*
+ * timer_ticks_to_usec
+ *
+ * convert timer ticks to usec.
+ */
+static inline unsigned long timer_ticks_to_usec(unsigned long ticks)
+{
+       unsigned long res;
+
+       res = ticks * timer_usec_ticks;
+       res += 1 << (TIMER_USEC_SHIFT - 4);     /* round up slightly */
+
+       return res >> TIMER_USEC_SHIFT;
+}
+
+/*
+ * Returns microsecond  since last clock interrupt.  Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ * IRQs are disabled before entering here from do_gettimeofday()
+ */
+static unsigned long s5p_gettimeoffset(void)
+{
+       unsigned long tdone;
+       unsigned long tval;
+       unsigned long clk_tick_totcnt;
+
+       clk_tick_totcnt = (timer_icnt + 1) * timer_startval;
+
+       /* work out how many ticks have gone since last timer interrupt */
+       tval = __raw_readl(S5P_SYSTIMER_ICNTO) * timer_startval;
+       tval += __raw_readl(S5P_SYSTIMER_TICNTO);
+
+       tdone = clk_tick_totcnt - tval;
+
+       /* check to see if there is an interrupt pending */
+       if (s5p_ostimer_pending()) {
+               /* re-read the timer, and try and fix up for the missed
+                * interrupt. Note, the interrupt may go off before the
+                * timer has re-loaded from wrapping.
+                */
+
+               tval = __raw_readl(S5P_SYSTIMER_ICNTO) * timer_startval;
+               tval += __raw_readl(S5P_SYSTIMER_TICNTO);
+
+               tdone = clk_tick_totcnt - tval;
+
+               if (tval != 0)
+                       tdone += clk_tick_totcnt;
+       }
+
+       return timer_ticks_to_usec(tdone);
+}
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t s5p_systimer_interrupt(int irq, void *dev_id)
+{
+       unsigned int temp_cstat;
+
+       temp_cstat = __raw_readl(S5P_SYSTIMER_INT_CSTAT);
+       temp_cstat |= S5P_SYSTIMER_INT_INTCNT;
+       s5p_systimer_write(S5P_SYSTIMER_INT_CSTAT, temp_cstat);
+
+       timer_tick();
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction s5p_systimer_irq = {
+       .name           = "S5P System Timer",
+       .flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+       .handler        = s5p_systimer_interrupt,
+};
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+static void s5p_systimer_setup(void)
+{
+       unsigned long tcon;
+       unsigned long tcnt;
+       unsigned long tcfg;
+       unsigned long int_csata;
+
+       /* clock configuration setting and enable */
+       unsigned long pclk;
+       struct clk *clk;
+
+       clk = clk_get(NULL, "systimer");
+       if (IS_ERR(clk))
+               panic("failed to get clock for system timer");
+
+       clk_enable(clk);
+
+       pclk = clk_get_rate(clk);
+
+       tcfg = __raw_readl(S5P_SYSTIMER_TCFG);
+       tcfg |= S5P_SYSTIMER_SWRST;
+       s5p_systimer_write(S5P_SYSTIMER_TCFG, tcfg);
+
+       tcnt = TICK_MAX;  /* default value for tcnt */
+
+       /* initialize system timer clock */
+       tcfg = __raw_readl(S5P_SYSTIMER_TCFG);
+
+       tcfg &= ~S5P_SYSTIMER_TCLK_MASK;
+       tcfg |= S5P_SYSTIMER_TCLK_PCLK;
+
+       s5p_systimer_write(S5P_SYSTIMER_TCFG, tcfg);
+
+       /* TCFG must not be changed at run-time.
+        * If you want to change TCFG, stop timer(TCON[0] = 0)
+        */
+
+       s5p_systimer_write(S5P_SYSTIMER_TCON, 0);
+
+       /* read the current timer configuration bits */
+       tcon = __raw_readl(S5P_SYSTIMER_TCON);
+       tcfg = __raw_readl(S5P_SYSTIMER_TCFG);
+
+       /* configure clock tick */
+       timer_usec_ticks = timer_mask_usec_ticks(S5P_SYSTIMER_PRESCALER, pclk);
+
+       tcfg &= ~S5P_SYSTIMER_TCLK_MASK;
+       tcfg |= S5P_SYSTIMER_TCLK_PCLK;
+       tcfg &= ~S5P_SYSTIMER_PRESCALER_MASK;
+       tcfg |= S5P_SYSTIMER_PRESCALER - 1;
+
+       tcnt = ((pclk / S5P_SYSTIMER_PRESCALER) / S5P_SYSTIMER_TARGET_HZ) - 1;
+
+       /* check to see if timer is within 16bit range... */
+       if (tcnt > TICK_MAX) {
+               panic("setup_timer: HZ is too small, cannot configure timer!");
+               return;
+       }
+
+       s5p_systimer_write(S5P_SYSTIMER_TCFG, tcfg);
+
+       timer_startval = tcnt;
+       s5p_systimer_write(S5P_SYSTIMER_TICNTB, tcnt);
+
+       /* set Interrupt tick value */
+       timer_icnt = (S5P_SYSTIMER_TARGET_HZ / HZ) - 1;
+       s5p_systimer_write(S5P_SYSTIMER_ICNTB, timer_icnt);
+
+       tcon = (S5P_SYSTIMER_INT_AUTO | S5P_SYSTIMER_START
+               | S5P_SYSTIMER_INT_START);
+       s5p_systimer_write(S5P_SYSTIMER_TCON, tcon);
+
+       /* Interrupt Start and Enable */
+       int_csata = __raw_readl(S5P_SYSTIMER_INT_CSTAT);
+       int_csata |= (S5P_SYSTIMER_INT_ICNTEIE | S5P_SYSTIMER_INT_INTENABLE);
+       s5p_systimer_write(S5P_SYSTIMER_INT_CSTAT, int_csata);
+}
+
+static void __init s5p_systimer_init(void)
+{
+       s5p_systimer_setup();
+       setup_irq(IRQ_SYSTIMER, &s5p_systimer_irq);
+}
+
+struct sys_timer s5p_systimer = {
+       .init           = s5p_systimer_init,
+       .offset         = s5p_gettimeoffset,
+       .resume         = s5p_systimer_setup
+};
diff --git a/arch/arm/plat-samsung/include/plat/cpu.h 
b/arch/arm/plat-samsung/include/plat/cpu.h
index d316b4a..6baa357 100644
--- a/arch/arm/plat-samsung/include/plat/cpu.h
+++ b/arch/arm/plat-samsung/include/plat/cpu.h
@@ -68,6 +68,9 @@ extern void s3c24xx_init_uartdevs(char *name,
 struct sys_timer;
 extern struct sys_timer s3c24xx_timer;
 
+/* timer for s5p */
+extern struct sys_timer s5p_systimer;
+
 /* system device classes */
 
 extern struct sysdev_class s3c2410_sysclass;
-- 
1.6.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to