From c0bb87f7b8a145b56d45484713e1b1f37ce7e626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Sun, 30 Sep 2007 20:36:00 +0100 Subject: [PATCH] [ARM] 4593/1: ns9xxx: implement generic clockevents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Russell King --- arch/arm/Kconfig | 1 + arch/arm/mach-ns9xxx/time.c | 164 +++++++++++++++++++++++++----------- 2 files changed, 116 insertions(+), 49 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a43a814bc7cf..4b984b120999 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -320,6 +320,7 @@ config ARCH_NS9XXX bool "NetSilicon NS9xxx" select GENERIC_GPIO select GENERIC_TIME + select GENERIC_CLOCKEVENTS help Say Y here if you intend to run this kernel on a NetSilicon NS9xxx System. diff --git a/arch/arm/mach-ns9xxx/time.c b/arch/arm/mach-ns9xxx/time.c index d29345501762..6f295158b168 100644 --- a/arch/arm/mach-ns9xxx/time.c +++ b/arch/arm/mach-ns9xxx/time.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,38 +21,9 @@ #include #include "generic.h" -#define TIMERCLOCKSELECT 64 -#define TIMER_CLOCKSOURCE 1 - -static irqreturn_t -ns9xxx_timer_interrupt(int irq, void *dev_id) -{ - int timerno = irq - IRQ_TIMER0; - u32 tc; - - write_seqlock(&xtime_lock); - timer_tick(); - write_sequnlock(&xtime_lock); - - /* clear irq */ - tc = SYS_TC(timerno); - if (REGGET(tc, SYS_TCx, REN) == SYS_TCx_REN_DIS) { - REGSET(tc, SYS_TCx, TEN, DIS); - SYS_TC(timerno) = tc; - } - REGSET(tc, SYS_TCx, INTC, SET); - SYS_TC(timerno) = tc; - REGSET(tc, SYS_TCx, INTC, UNSET); - SYS_TC(timerno) = tc; - - return IRQ_HANDLED; -} - -static struct irqaction ns9xxx_timer_irq = { - .name = "NS9xxx Timer Tick", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = ns9xxx_timer_interrupt, -}; +#define TIMER_CLOCKSOURCE 0 +#define TIMER_CLOCKEVENT 1 +static u32 latch; static cycle_t ns9xxx_clocksource_read(void) { @@ -67,27 +39,96 @@ static struct clocksource ns9xxx_clocksource = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; +static void ns9xxx_clockevent_setmode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + u32 tc = SYS_TC(TIMER_CLOCKEVENT); + + switch(mode) { + case CLOCK_EVT_MODE_PERIODIC: + SYS_TRC(TIMER_CLOCKEVENT) = latch; + REGSET(tc, SYS_TCx, REN, EN); + REGSET(tc, SYS_TCx, INTS, EN); + REGSET(tc, SYS_TCx, TEN, EN); + break; + + case CLOCK_EVT_MODE_ONESHOT: + REGSET(tc, SYS_TCx, REN, DIS); + REGSET(tc, SYS_TCx, INTS, EN); + + /* fall through */ + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_RESUME: + default: + REGSET(tc, SYS_TCx, TEN, DIS); + break; + } + + SYS_TC(TIMER_CLOCKEVENT) = tc; +} + +static int ns9xxx_clockevent_setnextevent(unsigned long evt, + struct clock_event_device *clk) +{ + u32 tc = SYS_TC(TIMER_CLOCKEVENT); + + if (REGGET(tc, SYS_TCx, TEN)) { + REGSET(tc, SYS_TCx, TEN, DIS); + SYS_TC(TIMER_CLOCKEVENT) = tc; + } + + REGSET(tc, SYS_TCx, TEN, EN); + + SYS_TRC(TIMER_CLOCKEVENT) = evt; + + SYS_TC(TIMER_CLOCKEVENT) = tc; + + return 0; +} + +static struct clock_event_device ns9xxx_clockevent_device = { + .name = "ns9xxx-timer" __stringify(TIMER_CLOCKEVENT), + .shift = 20, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = ns9xxx_clockevent_setmode, + .set_next_event = ns9xxx_clockevent_setnextevent, +}; + +static irqreturn_t ns9xxx_clockevent_handler(int irq, void *dev_id) +{ + int timerno = irq - IRQ_TIMER0; + u32 tc; + + struct clock_event_device *evt = &ns9xxx_clockevent_device; + + /* clear irq */ + tc = SYS_TC(timerno); + if (REGGET(tc, SYS_TCx, REN) == SYS_TCx_REN_DIS) { + REGSET(tc, SYS_TCx, TEN, DIS); + SYS_TC(timerno) = tc; + } + REGSET(tc, SYS_TCx, INTC, SET); + SYS_TC(timerno) = tc; + REGSET(tc, SYS_TCx, INTC, UNSET); + SYS_TC(timerno) = tc; + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction ns9xxx_clockevent_action = { + .name = "ns9xxx-timer" __stringify(TIMER_CLOCKEVENT), + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = ns9xxx_clockevent_handler, +}; + static void __init ns9xxx_timer_init(void) { int tc; - /* disable timer */ - if ((tc = SYS_TC(0)) & SYS_TCx_TEN) - SYS_TC(0) = tc & ~SYS_TCx_TEN; - - SYS_TRC(0) = SH_DIV(ns9xxx_cpuclock(), (TIMERCLOCKSELECT * HZ), 0); - - REGSET(tc, SYS_TCx, TEN, EN); - REGSET(tc, SYS_TCx, TLCS, DIV64); /* This must match TIMERCLOCKSELECT */ - REGSET(tc, SYS_TCx, INTS, EN); - REGSET(tc, SYS_TCx, UDS, DOWN); - REGSET(tc, SYS_TCx, TDBG, STOP); - REGSET(tc, SYS_TCx, TSZ, 32); - REGSET(tc, SYS_TCx, REN, EN); - SYS_TC(0) = tc; - - setup_irq(IRQ_TIMER0, &ns9xxx_timer_irq); - tc = SYS_TC(TIMER_CLOCKSOURCE); if (REGGET(tc, SYS_TCx, TEN)) { REGSET(tc, SYS_TCx, TEN, DIS); @@ -111,6 +152,31 @@ static void __init ns9xxx_timer_init(void) ns9xxx_clocksource.shift); clocksource_register(&ns9xxx_clocksource); + + latch = SH_DIV(ns9xxx_cpuclock(), HZ, 0); + + tc = SYS_TC(TIMER_CLOCKEVENT); + REGSET(tc, SYS_TCx, TEN, DIS); + REGSET(tc, SYS_TCx, TDBG, STOP); + REGSET(tc, SYS_TCx, TLCS, CPU); + REGSET(tc, SYS_TCx, TM, IEE); + REGSET(tc, SYS_TCx, INTS, DIS); + REGSET(tc, SYS_TCx, UDS, DOWN); + REGSET(tc, SYS_TCx, TSZ, 32); + REGSET(tc, SYS_TCx, REN, EN); + SYS_TC(TIMER_CLOCKEVENT) = tc; + + ns9xxx_clockevent_device.mult = div_sc(ns9xxx_cpuclock(), + NSEC_PER_SEC, ns9xxx_clockevent_device.shift); + ns9xxx_clockevent_device.max_delta_ns = + clockevent_delta2ns(-1, &ns9xxx_clockevent_device); + ns9xxx_clockevent_device.min_delta_ns = + clockevent_delta2ns(1, &ns9xxx_clockevent_device); + + ns9xxx_clockevent_device.cpumask = cpumask_of_cpu(0); + clockevents_register_device(&ns9xxx_clockevent_device); + + setup_irq(IRQ_TIMER0 + TIMER_CLOCKEVENT, &ns9xxx_clockevent_action); } struct sys_timer ns9xxx_timer = {