diff options
-rw-r--r-- | arch/arm/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-s5pv210/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-s5pv210/cpu.c | 8 | ||||
-rw-r--r-- | arch/arm/mach-s5pv210/include/mach/map.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-s5pv210/include/mach/tick.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-s5pv210/mach-herring.c | 12 | ||||
-rw-r--r-- | arch/arm/plat-s5p/Kconfig | 17 | ||||
-rw-r--r-- | arch/arm/plat-s5p/Makefile | 8 | ||||
-rw-r--r-- | arch/arm/plat-s5p/clock.c | 10 | ||||
-rw-r--r-- | arch/arm/plat-s5p/devs.c | 26 | ||||
-rw-r--r-- | arch/arm/plat-s5p/hr-time-rtc.c | 507 | ||||
-rw-r--r-- | arch/arm/plat-s5p/include/plat/regs-systimer.h | 77 | ||||
-rw-r--r-- | arch/arm/plat-samsung/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/plat-samsung/include/plat/cpu.h | 4 | ||||
-rw-r--r-- | arch/arm/plat-samsung/include/plat/devs.h | 1 | ||||
-rw-r--r-- | arch/arm/plat-samsung/include/plat/regs-rtc.h | 18 |
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 */ |