diff options
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-ns9xxx/time.c | 150 |
2 files changed, 109 insertions, 42 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 <linux/irq.h> #include <linux/stringify.h> #include <linux/clocksource.h> +#include <linux/clockchips.h> #include <asm/arch-ns9xxx/regs-sys.h> #include <asm/arch-ns9xxx/clock.h> @@ -20,18 +21,87 @@ #include <asm/arch/system.h> #include "generic.h" -#define TIMERCLOCKSELECT 64 -#define TIMER_CLOCKSOURCE 1 +#define TIMER_CLOCKSOURCE 0 +#define TIMER_CLOCKEVENT 1 +static u32 latch; -static irqreturn_t -ns9xxx_timer_interrupt(int irq, void *dev_id) +static cycle_t ns9xxx_clocksource_read(void) +{ + return SYS_TR(TIMER_CLOCKSOURCE); +} + +static struct clocksource ns9xxx_clocksource = { + .name = "ns9xxx-timer" __stringify(TIMER_CLOCKSOURCE), + .rating = 300, + .read = ns9xxx_clocksource_read, + .mask = CLOCKSOURCE_MASK(32), + .shift = 20, + .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; - write_seqlock(&xtime_lock); - timer_tick(); - write_sequnlock(&xtime_lock); + struct clock_event_device *evt = &ns9xxx_clockevent_device; /* clear irq */ tc = SYS_TC(timerno); @@ -44,50 +114,21 @@ ns9xxx_timer_interrupt(int irq, void *dev_id) 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, -}; + evt->event_handler(evt); -static cycle_t ns9xxx_clocksource_read(void) -{ - return SYS_TR(TIMER_CLOCKSOURCE); + return IRQ_HANDLED; } -static struct clocksource ns9xxx_clocksource = { - .name = "ns9xxx-timer" __stringify(TIMER_CLOCKSOURCE), - .rating = 300, - .read = ns9xxx_clocksource_read, - .mask = CLOCKSOURCE_MASK(32), - .shift = 20, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, +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 = { |