aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/mach-s5pv210/Kconfig1
-rw-r--r--arch/arm/mach-s5pv210/cpu.c8
-rw-r--r--arch/arm/mach-s5pv210/include/mach/map.h3
-rw-r--r--arch/arm/mach-s5pv210/include/mach/tick.h6
-rw-r--r--arch/arm/mach-s5pv210/mach-herring.c12
-rw-r--r--arch/arm/plat-s5p/Kconfig17
-rw-r--r--arch/arm/plat-s5p/Makefile8
-rw-r--r--arch/arm/plat-s5p/clock.c10
-rw-r--r--arch/arm/plat-s5p/devs.c26
-rw-r--r--arch/arm/plat-s5p/hr-time-rtc.c507
-rw-r--r--arch/arm/plat-s5p/include/plat/regs-systimer.h77
-rw-r--r--arch/arm/plat-samsung/Makefile2
-rw-r--r--arch/arm/plat-samsung/include/plat/cpu.h4
-rw-r--r--arch/arm/plat-samsung/include/plat/devs.h1
-rw-r--r--arch/arm/plat-samsung/include/plat/regs-rtc.h18
16 files changed, 699 insertions, 3 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ff8424b..31c552b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -754,7 +754,7 @@ config ARCH_S5PV210
select ARM_L1_CACHE_SHIFT_6
select ARCH_HAS_CPUFREQ
select GENERIC_CLOCKEVENTS
- select HAVE_SCHED_CLOCK
+ select HAVE_SCHED_CLOCK if !S5P_HIGH_RES_TIMERS
select HAVE_S3C2410_I2C if I2C
select HAVE_S3C_RTC if RTC_CLASS
select HAVE_S3C2410_WATCHDOG if WATCHDOG
diff --git a/arch/arm/mach-s5pv210/Kconfig b/arch/arm/mach-s5pv210/Kconfig
index c078144..f7341c0 100644
--- a/arch/arm/mach-s5pv210/Kconfig
+++ b/arch/arm/mach-s5pv210/Kconfig
@@ -112,6 +112,7 @@ config MACH_SMDKC110
select S3C_DEV_I2C1
select S3C_DEV_I2C2
select S3C_DEV_RTC
+ select HAVE_S3C_RTC
select S3C_DEV_WDT
select SAMSUNG_DEV_IDE
select S5PV210_SETUP_I2C1
diff --git a/arch/arm/mach-s5pv210/cpu.c b/arch/arm/mach-s5pv210/cpu.c
index 61e6c24..dcaa469 100644
--- a/arch/arm/mach-s5pv210/cpu.c
+++ b/arch/arm/mach-s5pv210/cpu.c
@@ -95,7 +95,15 @@ static struct map_desc s5pv210_iodesc[] __initdata = {
.pfn =__phys_to_pfn(S5PV210_PA_HSPHY),
.length = SZ_4K,
.type = MT_DEVICE,
+ },
+#if defined(CONFIG_HRT_RTC)
+ {
+ .virtual = (unsigned long)S5P_VA_RTC,
+ .pfn = __phys_to_pfn(S5PV210_PA_RTC),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
}
+#endif
};
static void s5pv210_idle(void)
diff --git a/arch/arm/mach-s5pv210/include/mach/map.h b/arch/arm/mach-s5pv210/include/mach/map.h
index c4657d7..4f7894e 100644
--- a/arch/arm/mach-s5pv210/include/mach/map.h
+++ b/arch/arm/mach-s5pv210/include/mach/map.h
@@ -57,6 +57,9 @@
#define S5PV210_PA_WATCHDOG 0xE2700000
#define S5PV210_PA_RTC 0xE2800000
+#define S5PV210_VA_RTC S3C_ADDR(0x00c00000)
+#define S5P_VA_RTC S5PV210_VA_RTC
+
#define S5PV210_PA_UART 0xE2900000
#define S5PV210_PA_SROMC 0xE8000000
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-herring.c b/arch/arm/mach-s5pv210/mach-herring.c
index 4d5c76b..70af092 100644
--- a/arch/arm/mach-s5pv210/mach-herring.c
+++ b/arch/arm/mach-s5pv210/mach-herring.c
@@ -42,6 +42,7 @@
#include <plat/cpu.h>
#include <plat/fb.h>
#include <plat/iic.h>
+#include <plat/s5p-time.h>
#include <plat/gpio-cfg.h>
@@ -2734,6 +2735,10 @@ EXPORT_SYMBOL(s3c_config_sleep_gpio);
static struct platform_device *herring_devices[] __initdata = {
+
+#ifdef CONFIG_RTC_DRV_S3C
+ &s5p_device_rtc,
+#endif
&s5pv210_device_iis0,
&s5pv210_device_ac97,
&s3c_device_wdt,
@@ -2834,6 +2839,9 @@ static void __init herring_map_io(void)
s5p_init_io(NULL, 0, S5P_VA_CHIPID);
s3c24xx_init_clocks(24000000);
s3c24xx_init_uarts(herring_uartcfgs, ARRAY_SIZE(herring_uartcfgs));
+#ifndef CONFIG_S5P_HIGH_RES_TIMERS
+ s5p_set_timer_source(S5P_PWM3, S5P_PWM4);
+#endif
s5pv210_reserve_bootmem();
#ifdef CONFIG_MTD_ONENAND
@@ -3206,7 +3214,11 @@ MACHINE_START(HERRING, "herring")
.init_irq = s5pv210_init_irq,
.map_io = herring_map_io,
.init_machine = herring_machine_init,
+#ifdef CONFIG_S5P_HIGH_RES_TIMERS
.timer = &s5p_systimer,
+#else
+ .timer = &s5p_timer,
+#endif
MACHINE_END
void s3c_setup_uart_cfg_gpio(unsigned char port)
diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig
index a356d2d..ad6b083 100644
--- a/arch/arm/plat-s5p/Kconfig
+++ b/arch/arm/plat-s5p/Kconfig
@@ -102,3 +102,20 @@ config S5P_DEV_FB
default y
help
Compile in platform device definitions for FIMD controller
+
+config S5P_HIGH_RES_TIMERS
+ bool "HRtimer and Dynamic Tick support"
+ select GENERIC_CLOCKEVENTS
+ select HIGH_RES_TIMERS
+ select HRT_RTC
+ select NO_HZ
+ default n
+ help
+ Support for HRtimer and Dynamic Tick system.
+
+config HRT_RTC
+ bool
+ depends on S5P_HIGH_RES_TIMERS
+ default y
+ help
+ RTC and System timer are used as HRT
diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile
index 6a3e63a..33b6a34 100644
--- a/arch/arm/plat-s5p/Makefile
+++ b/arch/arm/plat-s5p/Makefile
@@ -24,7 +24,9 @@ obj-$(CONFIG_S5P_GPIO_INT) += irq-gpioint.o
obj-$(CONFIG_S5P_SYSTEM_MMU) += sysmmu.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_PM) += irq-pm.o
+ifndef CONFIG_S5P_HIGH_RES_TIMERS
obj-$(CONFIG_S5P_HRT) += s5p-time.o
+endif
# devices
@@ -37,3 +39,9 @@ obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o
obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o
obj-$(CONFIG_S5P_DEV_USB_EHCI) += dev-ehci.o
obj-$(CONFIG_S5P_SETUP_MIPIPHY) += setup-mipiphy.o
+
+ifdef CONFIG_S5P_HIGH_RES_TIMERS
+ifdef CONFIG_HRT_RTC
+obj-y += hr-time-rtc.o
+endif
+endif
diff --git a/arch/arm/plat-s5p/clock.c b/arch/arm/plat-s5p/clock.c
index 8d081d9..10037b3 100644
--- a/arch/arm/plat-s5p/clock.c
+++ b/arch/arm/plat-s5p/clock.c
@@ -38,6 +38,13 @@ struct clk clk_ext_xtal_mux = {
struct clk clk_xusbxti = {
.name = "xusbxti",
.id = -1,
+ .rate = 24000000,
+};
+
+struct clk clk_xrtcxti = {
+ .name = "xrtcxti",
+ .id = -1,
+ .rate = 32768,
};
struct clk s5p_clk_27m = {
@@ -170,6 +177,8 @@ unsigned long s5p_epll_get_rate(struct clk *clk)
static struct clk *s5p_clks[] __initdata = {
&clk_ext_xtal_mux,
+ &clk_xrtcxti,
+ &clk_xusbxti,
&clk_48m,
&s5p_clk_27m,
&clk_fout_apll,
@@ -178,7 +187,6 @@ static struct clk *s5p_clks[] __initdata = {
&clk_fout_dpll,
&clk_fout_vpll,
&clk_vpll,
- &clk_xusbxti,
};
void __init s5p_register_clocks(unsigned long xtal_freq)
diff --git a/arch/arm/plat-s5p/devs.c b/arch/arm/plat-s5p/devs.c
index 9e9934d..3d62c7e 100644
--- a/arch/arm/plat-s5p/devs.c
+++ b/arch/arm/plat-s5p/devs.c
@@ -34,6 +34,32 @@
#include <plat/fimc.h>
#include <plat/csis.h>
+/* RTC */
+static struct resource s5p_rtc_resource[] = {
+ [0] = {
+ .start = S3C_PA_RTC,
+ .end = S3C_PA_RTC + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_RTC_ALARM,
+ .end = IRQ_RTC_ALARM,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_RTC_TIC,
+ .end = IRQ_RTC_TIC,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+struct platform_device s5p_device_rtc = {
+ .name = "s3c2410-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(s5p_rtc_resource),
+ .resource = s5p_rtc_resource,
+};
+
#if defined(CONFIG_VIDEO_MFC51) || defined(CONFIG_VIDEO_MFC50)
static struct resource s5p_mfc_resources[] = {
[0] = {
diff --git a/arch/arm/plat-s5p/hr-time-rtc.c b/arch/arm/plat-s5p/hr-time-rtc.c
new file mode 100644
index 0000000..79666ee
--- /dev/null
+++ b/arch/arm/plat-s5p/hr-time-rtc.c
@@ -0,0 +1,507 @@
+/*
+ * linux/arch/arm/plat-s5p/hr-time-rtc.c
+ *
+ * S5P Timers
+ *
+ * Copyright (c) 2006 Samsung Electronics
+ *
+ *
+ * S5P (and compatible) HRT 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.
+ *
+ * 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 <asm/system.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <asm/mach-types.h>
+#include <mach/map.h>
+#include <plat/regs-timer.h>
+#include <plat/regs-rtc.h>
+#include <plat/regs-systimer.h>
+#include <mach/regs-irq.h>
+#include <mach/regs-clock.h>
+#include <mach/tick.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+static unsigned long long time_stamp;
+static unsigned long long s5p_sched_timer_overflows;
+static unsigned long long old_overflows;
+static cycle_t last_ticks;
+
+/* Sched timer interrupt is not processed right after
+ * timer counter expired
+ */
+static unsigned int pending_irq;
+#define USE_SYSTIMER_IRQ
+
+/* sched_timer_running
+ * 0 : sched timer stopped or not initialized
+ * 1 : sched timer started
+ */
+static unsigned int sched_timer_running;
+
+void __iomem *rtc_base = S5P_VA_RTC;
+static struct clk *clk_event;
+static struct clk *clk_sched;
+static int tick_timer_mode; /* 0: oneshot, 1: autoreload */
+
+#define RTC_CLOCK (32768)
+#define RTC_DEFAULT_TICK ((RTC_CLOCK / HZ) - 1)
+/*
+ * Helper functions
+ * s5p_systimer_read() : Read from System timer register
+ * s5p_systimer_write(): Write to System timer register
+ *
+ */
+static unsigned int s5p_systimer_read(unsigned int *reg_offset)
+{
+ return __raw_readl(reg_offset);
+}
+
+static unsigned int s5p_systimer_write(unsigned int *reg_offset,
+ unsigned int value)
+{
+ unsigned int temp_regs;
+
+ __raw_writel(value, reg_offset);
+
+ if (reg_offset == S5P_SYSTIMER_TCON) {
+ while (!(__raw_readl(S5P_SYSTIMER_INT_CSTAT) &
+ S5P_SYSTIMER_INT_TCON));
+ temp_regs = __raw_readl(S5P_SYSTIMER_INT_CSTAT);
+ temp_regs |= S5P_SYSTIMER_INT_TCON;
+ __raw_writel(temp_regs, S5P_SYSTIMER_INT_CSTAT);
+
+ } else if (reg_offset == S5P_SYSTIMER_ICNTB) {
+ while (!(__raw_readl(S5P_SYSTIMER_INT_CSTAT) &
+ S5P_SYSTIMER_INT_ICNTB));
+ temp_regs = __raw_readl(S5P_SYSTIMER_INT_CSTAT);
+ temp_regs |= S5P_SYSTIMER_INT_ICNTB;
+ __raw_writel(temp_regs, S5P_SYSTIMER_INT_CSTAT);
+
+ } else if (reg_offset == S5P_SYSTIMER_TICNTB) {
+ while (!(__raw_readl(S5P_SYSTIMER_INT_CSTAT) &
+ S5P_SYSTIMER_INT_TICNTB));
+ temp_regs = __raw_readl(S5P_SYSTIMER_INT_CSTAT);
+ temp_regs |= S5P_SYSTIMER_INT_TICNTB;
+ __raw_writel(temp_regs, S5P_SYSTIMER_INT_CSTAT);
+ }
+
+ return 0;
+}
+
+#if 0 //Commenting unused API
+static void s5p_rtc_set_tick(int enabled)
+{
+ unsigned int tmp;
+
+ tmp = __raw_readl(rtc_base + S3C2410_RTCCON) & ~S3C_RTCCON_TICEN;
+ if (enabled)
+ tmp |= S3C_RTCCON_TICEN;
+ __raw_writel(tmp, rtc_base + S3C2410_RTCCON);
+}
+#endif
+
+unsigned int get_rtc_cnt(void)
+{
+ unsigned int ticcnt, current_cnt;
+ ticcnt = __raw_readl(rtc_base + S3C2410_TICNT);
+ current_cnt = __raw_readl(rtc_base + S3C2410_CURTICCNT);
+ return (ticcnt - current_cnt);
+}
+
+static void s5p_tick_timer_setup(void);
+
+static void s5p_tick_timer_start(unsigned long load_val,
+ int autoreset)
+{
+ unsigned int tmp;
+
+ tmp = __raw_readl(rtc_base + S3C2410_RTCCON) &
+ ~(S3C_RTCCON_TICEN | S3C2410_RTCCON_RTCEN);
+ __raw_writel(tmp, rtc_base + S3C2410_RTCCON);
+
+ __raw_writel(load_val, rtc_base + S3C2410_TICNT);
+
+ tmp |= S3C_RTCCON_TICEN;
+
+ __raw_writel(tmp, rtc_base + S3C2410_RTCCON);
+}
+
+static inline void s5p_tick_timer_stop(void)
+{
+ unsigned int tmp;
+
+ tmp = __raw_readl(rtc_base + S3C2410_RTCCON) &
+ ~(S3C_RTCCON_TICEN | S3C2410_RTCCON_RTCEN);
+
+ __raw_writel(tmp, rtc_base + S3C2410_RTCCON);
+
+}
+
+static void s5p_sched_timer_start(unsigned long load_val,
+ int autoreset)
+{
+ unsigned long tcon;
+ unsigned long tcnt;
+ unsigned long tcfg;
+
+
+ tcnt = TICK_MAX; /* default value for tcnt */
+
+ /* initialize system timer clock */
+ tcfg = s5p_systimer_read(S5P_SYSTIMER_TCFG);
+
+ tcfg &= ~S5P_SYSTIMER_TCLK_MASK;
+ tcfg |= S5P_SYSTIMER_TCLK_USB;
+
+ 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 = s5p_systimer_read(S5P_SYSTIMER_TCON);
+ tcfg = s5p_systimer_read(S5P_SYSTIMER_TCFG);
+
+ tcfg &= ~S5P_SYSTIMER_TCLK_MASK;
+ tcfg |= S5P_SYSTIMER_TCLK_USB;
+ tcfg &= ~S5P_SYSTIMER_PRESCALER_MASK;
+
+ /* check to see if timer is within 16bit range... */
+ if (tcnt > TICK_MAX) {
+ panic("setup_timer: cannot configure timer!");
+ return;
+ }
+
+ s5p_systimer_write(S5P_SYSTIMER_TCFG, tcfg);
+
+ s5p_systimer_write(S5P_SYSTIMER_TICNTB, tcnt);
+
+#if !defined(USE_SYSTIMER_IRQ)
+ /* set timer con */
+ tcon = (S5P_SYSTIMER_START | S5P_SYSTIMER_AUTO_RELOAD);
+ s5p_systimer_write(S5P_SYSTIMER_TCON, tcon);
+#else
+ /* set timer con */
+ tcon = S5P_SYSTIMER_INT_AUTO | S5P_SYSTIMER_START |
+ S5P_SYSTIMER_AUTO_RELOAD;
+ s5p_systimer_write(S5P_SYSTIMER_TCON, tcon);
+
+ tcon |= S5P_SYSTIMER_INT_START;
+ s5p_systimer_write(S5P_SYSTIMER_TCON, tcon);
+
+ /* Interrupt Start and Enable */
+ s5p_systimer_write(S5P_SYSTIMER_INT_CSTAT,
+ (S5P_SYSTIMER_INT_ICNTEIE |
+ S5P_SYSTIMER_INT_INTENABLE));
+#endif
+ sched_timer_running = 1;
+}
+
+/*
+ * RTC tick : count down to zero, interrupt, reload
+ */
+static int s5p_tick_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ /* printk(KERN_INFO "%d\n", cycles); */
+ if (cycles == 0) /* Should be larger than 0 */
+ cycles = 1;
+ s5p_tick_timer_start(cycles, 0);
+ return 0;
+}
+
+static void s5p_tick_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ tick_timer_mode = 1;
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ s5p_tick_timer_stop();
+ tick_timer_mode = 0;
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ /* Sched timer stopped */
+ sched_timer_running = 0;
+
+ /* Reset sched_clock variables after sleep/wakeup */
+ last_ticks = 0;
+ s5p_sched_timer_overflows = 0;
+ old_overflows = 0;
+ pending_irq = 0;
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ s5p_tick_timer_setup();
+ s5p_sched_timer_start(~0, 1);
+ break;
+ }
+}
+
+static struct clock_event_device clockevent_tick_timer = {
+ .name = "S5PC110 event timer",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ .set_next_event = s5p_tick_set_next_event,
+ .set_mode = s5p_tick_set_mode,
+};
+
+irqreturn_t s5p_tick_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = &clockevent_tick_timer;
+
+ __raw_writel(S3C_INTP_TIC, rtc_base + S3C_INTP);
+ /* In case of oneshot mode */
+ if (tick_timer_mode == 0)
+ s5p_tick_timer_stop();
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction s5p_tick_timer_irq = {
+ .name = "rtc-tick",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = s5p_tick_timer_interrupt,
+};
+
+static void s5p_init_dynamic_tick_timer(unsigned long rate)
+{
+ tick_timer_mode = 1;
+
+ s5p_tick_timer_stop();
+
+ s5p_tick_timer_start((rate / HZ) - 1, 1);
+
+ clockevent_tick_timer.mult = div_sc(rate, NSEC_PER_SEC,
+ clockevent_tick_timer.shift);
+ clockevent_tick_timer.max_delta_ns =
+ clockevent_delta2ns(-1, &clockevent_tick_timer);
+ clockevent_tick_timer.min_delta_ns =
+ clockevent_delta2ns(1, &clockevent_tick_timer);
+
+ clockevent_tick_timer.cpumask = cpumask_of(0);
+ clockevents_register_device(&clockevent_tick_timer);
+
+ printk(KERN_INFO "mult[%lu]\n", (long unsigned int)clockevent_tick_timer.mult);
+ printk(KERN_INFO "max_delta_ns[%lu]\n", (long unsigned int)clockevent_tick_timer.max_delta_ns);
+ printk(KERN_INFO "min_delta_ns[%lu]\n", (long unsigned int)clockevent_tick_timer.min_delta_ns);
+ printk(KERN_INFO "rate[%lu]\n", (long unsigned int)rate);
+ printk(KERN_INFO "HZ[%d]\n", HZ);
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * SYSTEM TIMER ... free running 32-bit clock source and scheduler clock
+ * ---------------------------------------------------------------------------
+ */
+irqreturn_t s5p_sched_timer_interrupt(int irq, void *dev_id)
+{
+ volatile unsigned int temp_cstat;
+
+ temp_cstat = s5p_systimer_read(S5P_SYSTIMER_INT_CSTAT);
+ temp_cstat |= S5P_SYSTIMER_INT_INTCNT;
+
+ s5p_systimer_write(S5P_SYSTIMER_INT_CSTAT, temp_cstat);
+
+ if (unlikely(pending_irq))
+ pending_irq = 0;
+ else
+ s5p_sched_timer_overflows++;
+
+ return IRQ_HANDLED;
+}
+
+struct irqaction s5p_systimer_irq = {
+ .name = "System timer",
+ .flags = IRQF_DISABLED ,
+ .handler = s5p_sched_timer_interrupt,
+};
+
+
+static cycle_t s5p_sched_timer_read(void)
+{
+
+ return (cycle_t)~__raw_readl(S5P_SYSTIMER_TICNTO);
+}
+
+struct clocksource clocksource_s5p = {
+ .name = "clock_source_systimer",
+ .rating = 300,
+ .read = s5p_sched_timer_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void s5p_init_clocksource(unsigned long rate)
+{
+ static char err[] __initdata = KERN_ERR
+ "%s: can't register clocksource!\n";
+
+ clocksource_s5p.mult
+ = clocksource_khz2mult(rate/1000, clocksource_s5p.shift);
+
+ s5p_sched_timer_start(~0, 1);
+
+ if (clocksource_register(&clocksource_s5p))
+ printk(err, clocksource_s5p.name);
+}
+
+/*
+ * Returns current time from boot in nsecs. It's OK for this to wrap
+ * around for now, as it's just a relative time stamp.
+ */
+#if 1
+unsigned long long sched_clock(void)
+{
+ unsigned long irq_flags;
+ cycle_t ticks, elapsed_ticks = 0;
+ unsigned long long increment = 0;
+ unsigned int overflow_cnt = 0;
+
+ local_irq_save(irq_flags);
+
+ if (likely(sched_timer_running)) {
+ overflow_cnt = (s5p_sched_timer_overflows - old_overflows);
+ ticks = s5p_sched_timer_read();
+
+ if (overflow_cnt) {
+ increment = (overflow_cnt - 1) *
+ (clocksource_cyc2ns(clocksource_s5p.read(&clocksource_s5p),
+ clocksource_s5p.mult, clocksource_s5p.shift));
+ elapsed_ticks = (clocksource_s5p.mask - last_ticks) + ticks;
+ } else {
+ if (unlikely(last_ticks > ticks)) {
+ pending_irq = 1;
+ elapsed_ticks = (clocksource_s5p.mask - last_ticks) + ticks;
+ s5p_sched_timer_overflows++;
+ } else {
+ elapsed_ticks = (ticks - last_ticks);
+ }
+ }
+
+ time_stamp += (clocksource_cyc2ns(elapsed_ticks,
+ clocksource_s5p.mult, clocksource_s5p.shift) + increment);
+
+ old_overflows = s5p_sched_timer_overflows;
+ last_ticks = ticks;
+ }
+ local_irq_restore(irq_flags);
+
+ return time_stamp;
+}
+#endif
+/*
+ * Event/Sched Timer initialization
+ */
+static void s5p_timer_setup(void)
+{
+ unsigned long rate;
+
+ /* Setup event timer using XrtcXTI */
+ if (clk_event == NULL)
+ clk_event = clk_get(NULL, "xrtcxti");
+
+ if (IS_ERR(clk_event))
+ panic("failed to get clock for event timer");
+
+ rate = clk_get_rate(clk_event);
+ s5p_init_dynamic_tick_timer(rate);
+
+ /* Setup sched-timer using XusbXTI */
+ if (clk_sched == NULL)
+ clk_sched = clk_get(NULL, "xusbxti");
+ if (IS_ERR(clk_sched))
+ panic("failed to get clock for sched-timer");
+ rate = clk_get_rate(clk_sched);
+
+ s5p_init_clocksource(rate);
+}
+
+static void s5p_tick_timer_setup(void)
+{
+ unsigned long rate;
+
+ rate = clk_get_rate(clk_event);
+ s5p_tick_timer_start((rate / HZ) - 1, 1);
+}
+
+static void __init s5p_timer_init(void)
+{
+
+ /* clock configuration setting and enable */
+ struct clk *clk_systimer;
+ struct clk *clk_rtc;
+
+ /* Initialize variables before starting each timers */
+ last_ticks = 0;
+ s5p_sched_timer_overflows = 0;
+ old_overflows = 0;
+ time_stamp = 0;
+ sched_timer_running = 0;
+ pending_irq = 0;
+
+ /* Setup system timer */
+ clk_systimer = clk_get(NULL, "systimer");
+ if (IS_ERR(clk_systimer))
+ panic("failed to get clock[%s] for system timer", "systimer");
+
+ clk_enable(clk_systimer);
+ clk_put(clk_systimer);
+
+ /* Setup rtc timer */
+ clk_rtc = clk_get(NULL, "rtc");
+ if (IS_ERR(clk_rtc))
+ panic("failed to get clock[%s] for system timer", "rtc");
+
+ clk_enable(clk_rtc);
+ clk_put(clk_rtc);
+
+ s5p_timer_setup();
+ setup_irq(IRQ_RTC_TIC, &s5p_tick_timer_irq);
+#if defined(USE_SYSTIMER_IRQ)
+ setup_irq(IRQ_SYSTIMER, &s5p_systimer_irq);
+#endif
+}
+
+struct sys_timer s5p_systimer = {
+ .init = s5p_timer_init,
+};
+
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..7091c40
--- /dev/null
+++ b/arch/arm/plat-s5p/include/plat/regs-systimer.h
@@ -0,0 +1,77 @@
+/* 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 200
+#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_AUTO_RELOAD (1<<2)
+#define S5P_SYSTIMER_IMM_UPDATE (1<<1)
+#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-samsung/Makefile b/arch/arm/plat-samsung/Makefile
index 53eb15b..c4bc9cb 100644
--- a/arch/arm/plat-samsung/Makefile
+++ b/arch/arm/plat-samsung/Makefile
@@ -12,7 +12,9 @@ obj- :=
# Objects we always build independent of SoC choice
obj-y += init.o
+ifndef CONFIG_S5P_HIGH_RES_TIMERS
obj-$(CONFIG_ARCH_USES_GETTIMEOFFSET) += time.o
+endif
obj-y += clock.o
obj-y += pwm-clock.o
obj-y += gpio.o
diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h
index c0a5741..8865194 100644
--- a/arch/arm/plat-samsung/include/plat/cpu.h
+++ b/arch/arm/plat-samsung/include/plat/cpu.h
@@ -66,7 +66,11 @@ extern void s3c24xx_init_uartdevs(char *name,
/* timer for 2410/2440 */
struct sys_timer;
+#if defined(CONFIG_S5P_HIGH_RES_TIMERS)
+extern struct sys_timer s5p_systimer;
+#else
extern struct sys_timer s3c24xx_timer;
+#endif
extern struct syscore_ops s3c2410_pm_syscore_ops;
extern struct syscore_ops s3c2412_pm_syscore_ops;
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index e3b31c2..94c7fcb 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -142,6 +142,7 @@ extern struct platform_device s5p_device_mipi_csis1;
extern struct platform_device s5p_device_ehci;
extern struct platform_device exynos4_device_sysmmu;
+extern struct platform_device s5p_device_rtc;
/* s3c2440 specific devices */
diff --git a/arch/arm/plat-samsung/include/plat/regs-rtc.h b/arch/arm/plat-samsung/include/plat/regs-rtc.h
index 30b7cc1..b8daf4b 100644
--- a/arch/arm/plat-samsung/include/plat/regs-rtc.h
+++ b/arch/arm/plat-samsung/include/plat/regs-rtc.h
@@ -28,6 +28,22 @@
#define S3C64XX_RTCCON_TICMSK (0xF<<7)
#define S3C64XX_RTCCON_TICSHT (7)
+#if defined(CONFIG_CPU_S5PC100) || defined(CONFIG_CPU_S5PV210)
+#define S3C_MAX_CNT 32768
+#define S3C_RTCCON_TICEN (1<<8)
+#define S3C_RTC_TICNT S3C2410_RTCREG(0x40)
+#else
+#define S3C_INTP_ALM (1<<1)
+#define S3C_MAX_CNT 128
+#define S3C_RTCCON_TICEN (1<<7)
+#define S3C_RTC_TICNT S3C2410_RTCREG(0x44)
+#endif
+
+/* Common Reg for samsung AP*/
+#define S3C_INTP S3C2410_RTCREG(0x30)
+#define S3C_INTP_ALM (1<<1)
+#define S3C_INTP_TIC (1<<0)
+
#define S3C2410_TICNT S3C2410_RTCREG(0x44)
#define S3C2410_TICNT_ENABLE (1<<7)
@@ -63,6 +79,6 @@
#define S3C2410_RTCDAY S3C2410_RTCREG(0x80)
#define S3C2410_RTCMON S3C2410_RTCREG(0x84)
#define S3C2410_RTCYEAR S3C2410_RTCREG(0x88)
-
+#define S3C2410_CURTICCNT S3C2410_RTCREG(0x90)
#endif /* __ASM_ARCH_REGS_RTC_H */