From 4e8d76373c9fd7a1c1b401fc97ba01c0ecbb888f Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 28 Jan 2011 21:00:39 +0000 Subject: [PATCH] ARM: footbridge: convert to clockevents/clocksource The Footbridge platforms have some reasonable timers in the host bridge, which we use for most footbridge-based platforms. However, NetWinder's clock these using a spread-spectrum clock which makes them too unstable for time keeping. So we have to rely on the PIT. Convert both Footbridge timers and PIT timers to use the clocksource and clockevent infrastructure. Tested on Netwinder. Signed-off-by: Russell King --- arch/arm/Kconfig | 2 +- arch/arm/mach-footbridge/dc21285-timer.c | 88 +++++++++++---- arch/arm/mach-footbridge/isa-timer.c | 135 +++++++++++++++-------- 3 files changed, 157 insertions(+), 68 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5cff165b7eb0..6d9fbfe32a2d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -346,7 +346,7 @@ config ARCH_FOOTBRIDGE bool "FootBridge" select CPU_SA110 select FOOTBRIDGE - select ARCH_USES_GETTIMEOFFSET + select GENERIC_CLOCKEVENTS help Support for systems based on the DC21285 companion chip ("FootBridge"), such as the Simtec CATS and the Rebel NetWinder. diff --git a/arch/arm/mach-footbridge/dc21285-timer.c b/arch/arm/mach-footbridge/dc21285-timer.c index bc5e83fb5819..a921fe92b858 100644 --- a/arch/arm/mach-footbridge/dc21285-timer.c +++ b/arch/arm/mach-footbridge/dc21285-timer.c @@ -4,10 +4,11 @@ * Copyright (C) 1998 Russell King. * Copyright (C) 1998 Phil Blundell */ +#include +#include #include #include #include -#include #include @@ -16,32 +17,76 @@ #include "common.h" -/* - * Footbridge timer 1 support. - */ -static unsigned long timer1_latch; - -static unsigned long timer1_gettimeoffset (void) +static cycle_t cksrc_dc21285_read(struct clocksource *cs) { - unsigned long value = timer1_latch - *CSR_TIMER1_VALUE; - - return ((tick_nsec / 1000) * value) / timer1_latch; + return cs->mask - *CSR_TIMER2_VALUE; } -static irqreturn_t -timer1_interrupt(int irq, void *dev_id) +static int cksrc_dc21285_enable(struct clocksource *cs) { + *CSR_TIMER2_LOAD = cs->mask; + *CSR_TIMER2_CLR = 0; + *CSR_TIMER2_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16; + return 0; +} + +static int cksrc_dc21285_disable(struct clocksource *cs) +{ + *CSR_TIMER2_CNTL = 0; +} + +static struct clocksource cksrc_dc21285 = { + .name = "dc21285_timer2", + .rating = 200, + .read = cksrc_dc21285_read, + .enable = cksrc_dc21285_enable, + .disable = cksrc_dc21285_disable, + .mask = CLOCKSOURCE_MASK(24), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void ckevt_dc21285_set_mode(enum clock_event_mode mode, + struct clock_event_device *c) +{ + switch (mode) { + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_PERIODIC: + *CSR_TIMER1_CLR = 0; + *CSR_TIMER1_LOAD = (mem_fclk_21285 + 8 * HZ) / (16 * HZ); + *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | + TIMER_CNTL_DIV16; + break; + + default: + *CSR_TIMER1_CNTL = 0; + break; + } +} + +static struct clock_event_device ckevt_dc21285 = { + .name = "dc21285_timer1", + .features = CLOCK_EVT_FEAT_PERIODIC, + .rating = 200, + .irq = IRQ_TIMER1, + .set_mode = ckevt_dc21285_set_mode, +}; + +static irqreturn_t timer1_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *ce = dev_id; + *CSR_TIMER1_CLR = 0; - timer_tick(); + ce->event_handler(ce); return IRQ_HANDLED; } static struct irqaction footbridge_timer_irq = { - .name = "Timer1 timer tick", + .name = "dc21285_timer1", .handler = timer1_interrupt, .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .dev_id = &ckevt_dc21285, }; /* @@ -49,16 +94,19 @@ static struct irqaction footbridge_timer_irq = { */ static void __init footbridge_timer_init(void) { - timer1_latch = (mem_fclk_21285 + 8 * HZ) / (16 * HZ); + struct clock_event_device *ce = &ckevt_dc21285; - *CSR_TIMER1_CLR = 0; - *CSR_TIMER1_LOAD = timer1_latch; - *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | TIMER_CNTL_DIV16; + clocksource_register_hz(&cksrc_dc21285, (mem_fclk_21285 + 8) / 16); - setup_irq(IRQ_TIMER1, &footbridge_timer_irq); + setup_irq(ce->irq, &footbridge_timer_irq); + + clockevents_calc_mult_shift(ce, mem_fclk_21285, 5); + ce->max_delta_ns = clockevent_delta2ns(0xffffff, ce); + ce->min_delta_ns = clockevent_delta2ns(0x000004, ce); + + clockevents_register_device(ce); } struct sys_timer footbridge_timer = { .init = footbridge_timer_init, - .offset = timer1_gettimeoffset, }; diff --git a/arch/arm/mach-footbridge/isa-timer.c b/arch/arm/mach-footbridge/isa-timer.c index f488fa2082d7..441c6ce0d555 100644 --- a/arch/arm/mach-footbridge/isa-timer.c +++ b/arch/arm/mach-footbridge/isa-timer.c @@ -4,10 +4,13 @@ * Copyright (C) 1998 Russell King. * Copyright (C) 1998 Phil Blundell */ +#include +#include #include #include #include #include +#include #include @@ -15,77 +18,115 @@ #include "common.h" -/* - * ISA timer tick support - */ -#define mSEC_10_from_14 ((14318180 + 100) / 200) +#define PIT_MODE 0x43 +#define PIT_CH0 0x40 -static unsigned long isa_gettimeoffset(void) +#define PIT_LATCH ((PIT_TICK_RATE + HZ / 2) / HZ) + +static cycle_t pit_read(struct clocksource *cs) { + unsigned long flags; + static int old_count; + static u32 old_jifs; int count; + u32 jifs; - static int count_p = (mSEC_10_from_14/6); /* for the first call after boot */ - static unsigned long jiffies_p = 0; + raw_local_irq_save(flags); - /* - * cache volatile jiffies temporarily; we have IRQs turned off. - */ - unsigned long jiffies_t; + jifs = jiffies; + outb_p(0x00, PIT_MODE); /* latch the count */ + count = inb_p(PIT_CH0); /* read the latched count */ + count |= inb_p(PIT_CH0) << 8; - /* timer count may underflow right here */ - outb_p(0x00, 0x43); /* latch the count ASAP */ + if (count > old_count && jifs == old_jifs) + count = old_count; - count = inb_p(0x40); /* read the latched count */ + old_count = count; + old_jifs = jifs; - /* - * We do this guaranteed double memory access instead of a _p - * postfix in the previous port access. Wheee, hackady hack - */ - jiffies_t = jiffies; + raw_local_irq_restore(flags); - count |= inb_p(0x40) << 8; + count = (PIT_LATCH - 1) - count; - /* Detect timer underflows. If we haven't had a timer tick since - the last time we were called, and time is apparently going - backwards, the counter must have wrapped during this routine. */ - if ((jiffies_t == jiffies_p) && (count > count_p)) - count -= (mSEC_10_from_14/6); - else - jiffies_p = jiffies_t; - - count_p = count; - - count = (((mSEC_10_from_14/6)-1) - count) * (tick_nsec / 1000); - count = (count + (mSEC_10_from_14/6)/2) / (mSEC_10_from_14/6); - - return count; + return (cycle_t)(jifs * PIT_LATCH) + count; } -static irqreturn_t -isa_timer_interrupt(int irq, void *dev_id) +static struct clocksource pit_cs = { + .name = "pit", + .rating = 110, + .read = pit_read, + .mask = CLOCKSOURCE_MASK(32), +}; + +static void pit_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) { - timer_tick(); + unsigned long flags; + + raw_local_irq_save(flags); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + outb_p(0x34, PIT_MODE); + outb_p(PIT_LATCH & 0xff, PIT_CH0); + outb_p(PIT_LATCH >> 8, PIT_CH0); + break; + + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + outb_p(0x30, PIT_MODE); + outb_p(0, PIT_CH0); + outb_p(0, PIT_CH0); + break; + + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_RESUME: + break; + } + local_irq_restore(flags); +} + +static int pit_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + return 0; +} + +static struct clock_event_device pit_ce = { + .name = "pit", + .features = CLOCK_EVT_FEAT_PERIODIC, + .set_mode = pit_set_mode, + .set_next_event = pit_set_next_event, + .shift = 32, +}; + +static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *ce = dev_id; + ce->event_handler(ce); return IRQ_HANDLED; } -static struct irqaction isa_timer_irq = { - .name = "ISA timer tick", - .handler = isa_timer_interrupt, +static struct irqaction pit_timer_irq = { + .name = "pit", + .handler = pit_timer_interrupt, .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .dev_id = &pit_ce, }; static void __init isa_timer_init(void) { - /* enable PIT timer */ - /* set for periodic (4) and LSB/MSB write (0x30) */ - outb(0x34, 0x43); - outb((mSEC_10_from_14/6) & 0xFF, 0x40); - outb((mSEC_10_from_14/6) >> 8, 0x40); + pit_ce.cpumask = cpumask_of(smp_processor_id()); + pit_ce.mult = div_sc(PIT_TICK_RATE, NSEC_PER_SEC, pit_ce.shift); + pit_ce.max_delta_ns = clockevent_delta2ns(0x7fff, &pit_ce); + pit_ce.min_delta_ns = clockevent_delta2ns(0x000f, &pit_ce); - setup_irq(IRQ_ISA_TIMER, &isa_timer_irq); + clocksource_register_hz(&pit_cs, PIT_TICK_RATE); + + setup_irq(pit_ce.irq, &pit_timer_irq); + clockevents_register_device(&pit_ce); } struct sys_timer isa_timer = { .init = isa_timer_init, - .offset = isa_gettimeoffset, };