From eee92962f4937f569a74c23844efa33dcb654584 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Sat, 4 Feb 2012 17:26:15 +0800 Subject: [PATCH] rk30: add timer support --- arch/arm/mach-rk30/Makefile | 1 + arch/arm/mach-rk30/board-rk30-sdk.c | 1 + arch/arm/mach-rk30/include/mach/board.h | 2 + arch/arm/mach-rk30/include/mach/io.h | 7 +- arch/arm/mach-rk30/io.c | 3 + arch/arm/mach-rk30/timer.c | 215 ++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-rk30/timer.c diff --git a/arch/arm/mach-rk30/Makefile b/arch/arm/mach-rk30/Makefile index 2b98925e498d..ccf4844a46d6 100644 --- a/arch/arm/mach-rk30/Makefile +++ b/arch/arm/mach-rk30/Makefile @@ -2,5 +2,6 @@ obj-y += clock.o obj-y += common.o obj-y += io.o obj-y += reset.o +obj-y += timer.o obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o obj-$(CONFIG_MACH_RK30_SDK) += board-rk30-sdk.o diff --git a/arch/arm/mach-rk30/board-rk30-sdk.c b/arch/arm/mach-rk30/board-rk30-sdk.c index c4d0b9e47e75..7d5bf2d660fb 100644 --- a/arch/arm/mach-rk30/board-rk30-sdk.c +++ b/arch/arm/mach-rk30/board-rk30-sdk.c @@ -40,4 +40,5 @@ MACHINE_START(RK30, "RK30board") .fixup = rk30_fixup, .map_io = rk30_map_io, .init_irq = rk30_init_irq, + .timer = &rk30_timer, MACHINE_END diff --git a/arch/arm/mach-rk30/include/mach/board.h b/arch/arm/mach-rk30/include/mach/board.h index 6f1357fa9556..af296e162513 100644 --- a/arch/arm/mach-rk30/include/mach/board.h +++ b/arch/arm/mach-rk30/include/mach/board.h @@ -16,4 +16,6 @@ void __init rk30_map_io(void); struct machine_desc; void __init rk30_fixup(struct machine_desc *desc, struct tag *tags, char **cmdline, struct meminfo *mi); +extern struct sys_timer rk30_timer; + #endif diff --git a/arch/arm/mach-rk30/include/mach/io.h b/arch/arm/mach-rk30/include/mach/io.h index 6144a5772955..e5c7224a1529 100644 --- a/arch/arm/mach-rk30/include/mach/io.h +++ b/arch/arm/mach-rk30/include/mach/io.h @@ -143,8 +143,9 @@ #define RK30_GPIO6_BASE RK30_IO_TO_VIRT1(RK30_GPIO6_PHYS) #define RK30_GPIO6_SIZE SZ_8K -#define RK30_TIMER3_PHYS 0x2000e000 -#define RK30_TIMER3_SIZE SZ_8K +#define RK30_TIMER2_PHYS 0x2000e000 +#define RK30_TIMER2_BASE RK30_IO_TO_VIRT1(RK30_TIMER2_PHYS) +#define RK30_TIMER2_SIZE SZ_8K #define RK30_EFUSE_PHYS 0x20010000 #define RK30_EFUSE_SIZE SZ_16K #define RK30_TZPC_PHYS 0x20014000 @@ -166,8 +167,10 @@ #define RK30_GPIO0_BASE RK30_IO_TO_VIRT1(RK30_GPIO0_PHYS) #define RK30_GPIO0_SIZE SZ_16K #define RK30_TIMER0_PHYS 0x20038000 +#define RK30_TIMER0_BASE RK30_IO_TO_VIRT1(RK30_TIMER0_PHYS) #define RK30_TIMER0_SIZE SZ_8K #define RK30_TIMER1_PHYS 0x2003a000 +#define RK30_TIMER1_BASE RK30_IO_TO_VIRT1(RK30_TIMER1_PHYS) #define RK30_TIMER1_SIZE SZ_8K #define RK30_GPIO1_PHYS 0x2003c000 #define RK30_GPIO1_BASE RK30_IO_TO_VIRT1(RK30_GPIO1_PHYS) diff --git a/arch/arm/mach-rk30/io.c b/arch/arm/mach-rk30/io.c index 2ba328309cdd..5a92c9936ca4 100644 --- a/arch/arm/mach-rk30/io.c +++ b/arch/arm/mach-rk30/io.c @@ -40,6 +40,9 @@ static struct map_desc rk30_io_desc[] __initdata = { RK30_DEVICE(GPIO3), RK30_DEVICE(GPIO4), RK30_DEVICE(GPIO6), + RK30_DEVICE(TIMER0), + RK30_DEVICE(TIMER1), + RK30_DEVICE(TIMER2), }; void __init rk30_map_common_io(void) diff --git a/arch/arm/mach-rk30/timer.c b/arch/arm/mach-rk30/timer.c new file mode 100644 index 000000000000..c4d35965d1bb --- /dev/null +++ b/arch/arm/mach-rk30/timer.c @@ -0,0 +1,215 @@ +/* linux/arch/arm/mach-rk30/timer.c + * + * Copyright (C) 2012 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TIMER_LOAD_COUNT 0x0000 +#define TIMER_CUR_VALUE 0x0004 +#define TIMER_CONTROL_REG 0x0008 +#define TIMER_EOI 0x000C +#define TIMER_INT_STATUS 0x0010 + +#define TIMER_DISABLE 6 +#define TIMER_ENABLE 3 +#define TIMER_ENABLE_FREE_RUNNING 5 + +static inline void timer_write(u32 n, u32 v, u32 offset) +{ + void __iomem* base = (n == 0) ? RK30_TIMER0_BASE : (n == 1) ? RK30_TIMER1_BASE : RK30_TIMER2_BASE; + void __iomem* addr = base + offset; + __raw_writel(v, addr); + dsb(); +} + +static inline u32 timer_read(u32 n, u32 offset) +{ + void __iomem* base = (n == 0) ? RK30_TIMER0_BASE : (n == 1) ? RK30_TIMER1_BASE : RK30_TIMER2_BASE; + void __iomem* addr = base + offset; + return __raw_readl(addr); +} + +#define RK_TIMER_ENABLE(n) timer_write(n, TIMER_ENABLE, TIMER_CONTROL_REG) +#define RK_TIMER_ENABLE_FREE_RUNNING(n) timer_write(n, TIMER_ENABLE_FREE_RUNNING, TIMER_CONTROL_REG) +#define RK_TIMER_DISABLE(n) timer_write(n, TIMER_DISABLE, TIMER_CONTROL_REG) + +#define RK_TIMER_SETCOUNT(n, count) timer_write(n, count, TIMER_LOAD_COUNT) +#define RK_TIMER_GETCOUNT(n) timer_read(n, TIMER_LOAD_COUNT) + +#define RK_TIMER_READVALUE(n) timer_read(n, TIMER_CUR_VALUE) +#define RK_TIMER_INT_CLEAR(n) timer_read(n, TIMER_EOI) + +#define RK_TIMER_INT_STATUS(n) timer_read(n, TIMER_INT_STATUS) + +#define TIMER_CLKEVT 0 /* timer0 */ +#define IRQ_NR_TIMER_CLKEVT IRQ_TIMER0 +#define TIMER_CLKEVT_NAME "timer0" + +#define TIMER_CLKSRC 1 /* timer1 */ +#define IRQ_NR_TIMER_CLKSRC IRQ_TIMER1 +#define TIMER_CLKSRC_NAME "timer1" + +static int rk30_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) +{ + RK_TIMER_DISABLE(TIMER_CLKEVT); + RK_TIMER_SETCOUNT(TIMER_CLKEVT, cycles); + RK_TIMER_ENABLE(TIMER_CLKEVT); + return 0; +} + +static void rk30_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + RK_TIMER_DISABLE(TIMER_CLKEVT); + RK_TIMER_SETCOUNT(TIMER_CLKEVT, 24000000 / HZ - 1); + RK_TIMER_ENABLE(TIMER_CLKEVT); + break; + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_ONESHOT: + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + RK_TIMER_DISABLE(TIMER_CLKEVT); + break; + } +} + +static struct clock_event_device rk30_timer_clockevent = { + .name = TIMER_CLKEVT_NAME, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .rating = 200, + .set_next_event = rk30_timer_set_next_event, + .set_mode = rk30_timer_set_mode, +}; + +static irqreturn_t rk30_timer_clockevent_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + RK_TIMER_INT_CLEAR(TIMER_CLKEVT); + if (evt->mode == CLOCK_EVT_MODE_ONESHOT) + RK_TIMER_DISABLE(TIMER_CLKEVT); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction rk30_timer_clockevent_irq = { + .name = TIMER_CLKEVT_NAME, + .flags = IRQF_DISABLED | IRQF_TIMER, + .handler = rk30_timer_clockevent_interrupt, + .irq = IRQ_NR_TIMER_CLKEVT, + .dev_id = &rk30_timer_clockevent, +}; + +static __init int rk30_timer_init_clockevent(void) +{ + struct clock_event_device *ce = &rk30_timer_clockevent; + struct clk *clk = clk_get(NULL, TIMER_CLKEVT_NAME); + + clk_enable(clk); + + RK_TIMER_DISABLE(TIMER_CLKEVT); + + setup_irq(rk30_timer_clockevent_irq.irq, &rk30_timer_clockevent_irq); + + clockevents_calc_mult_shift(ce, 24000000, 4); + ce->max_delta_ns = clockevent_delta2ns(0xFFFFFFFFUL, ce); + ce->min_delta_ns = clockevent_delta2ns(1, ce) + 1; + + ce->cpumask = cpu_all_mask; + + clockevents_register_device(ce); + + return 0; +} + +static cycle_t rk30_timer_read(struct clocksource *cs) +{ + return ~RK_TIMER_READVALUE(TIMER_CLKSRC); +} + +#define MASK (u32)~0 + +static struct clocksource rk30_timer_clocksource = { + .name = TIMER_CLKSRC_NAME, + .rating = 200, + .read = rk30_timer_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init rk30_timer_init_clocksource(void) +{ + static char err[] __initdata = KERN_ERR "%s: can't register clocksource!\n"; + struct clocksource *cs = &rk30_timer_clocksource; + struct clk *clk = clk_get(NULL, TIMER_CLKSRC_NAME); + + clk_enable(clk); + + RK_TIMER_DISABLE(TIMER_CLKSRC); + RK_TIMER_SETCOUNT(TIMER_CLKSRC, 0xFFFFFFFF); + RK_TIMER_ENABLE_FREE_RUNNING(TIMER_CLKSRC); + + clocksource_calc_mult_shift(cs, 24000000, 60); + if (clocksource_register(cs)) + printk(err, cs->name); +} + +static DEFINE_CLOCK_DATA(cd); + +unsigned long long notrace sched_clock(void) +{ + u32 cyc = ~RK_TIMER_READVALUE(TIMER_CLKSRC); + const struct clocksource *cs = &rk30_timer_clocksource; + return cyc_to_fixed_sched_clock(&cd, cyc, MASK, cs->mult, cs->shift); +} + +static void notrace rk30_update_sched_clock(void) +{ + u32 cyc = ~RK_TIMER_READVALUE(TIMER_CLKSRC); + update_sched_clock(&cd, cyc, MASK); +} + +static void __init rk30_sched_clock_init(void) +{ + init_sched_clock(&cd, rk30_update_sched_clock, 32, 24000000); +} + +static void __init rk30_timer_init(void) +{ +#ifdef CONFIG_HAVE_ARM_TWD + twd_base = RK30_PTIMER_BASE; +#endif + rk30_timer_init_clocksource(); + rk30_timer_init_clockevent(); + rk30_sched_clock_init(); +} + +struct sys_timer rk30_timer = { + .init = rk30_timer_init +}; + -- 2.34.1