ARM: footbridge: convert to clockevents/clocksource
authorRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 28 Jan 2011 21:00:39 +0000 (21:00 +0000)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 31 Jan 2011 15:54:39 +0000 (15:54 +0000)
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 <rmk+kernel@arm.linux.org.uk>
arch/arm/Kconfig
arch/arm/mach-footbridge/dc21285-timer.c
arch/arm/mach-footbridge/isa-timer.c

index 5cff165b7eb04ef092e09957cf05cf6b8eddfcee..6d9fbfe32a2dbb746a8092802ca24cf4daa3870a 100644 (file)
@@ -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.
index bc5e83fb5819edacfc17c1315a16028b3dd4cc8c..a921fe92b858c730d5c5669602b8aa58f435f588 100644 (file)
@@ -4,10 +4,11 @@
  *  Copyright (C) 1998 Russell King.
  *  Copyright (C) 1998 Phil Blundell
  */
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
-#include <linux/spinlock.h>
 
 #include <asm/irq.h>
 
 
 #include "common.h"
 
-/*
- * Footbridge timer 1 support.
- */
-static unsigned long timer1_latch;
+static cycle_t cksrc_dc21285_read(struct clocksource *cs)
+{
+       return cs->mask - *CSR_TIMER2_VALUE;
+}
 
-static unsigned long timer1_gettimeoffset (void)
+static int cksrc_dc21285_enable(struct clocksource *cs)
 {
-       unsigned long value = timer1_latch - *CSR_TIMER1_VALUE;
+       *CSR_TIMER2_LOAD = cs->mask;
+       *CSR_TIMER2_CLR = 0;
+       *CSR_TIMER2_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16;
+       return 0;
+}
 
-       return ((tick_nsec / 1000) * value) / timer1_latch;
+static int cksrc_dc21285_disable(struct clocksource *cs)
+{
+       *CSR_TIMER2_CNTL = 0;
 }
 
-static irqreturn_t
-timer1_interrupt(int irq, void *dev_id)
+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;
+
+       clocksource_register_hz(&cksrc_dc21285, (mem_fclk_21285 + 8) / 16);
+
+       setup_irq(ce->irq, &footbridge_timer_irq);
 
-       *CSR_TIMER1_CLR  = 0;
-       *CSR_TIMER1_LOAD = timer1_latch;
-       *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | TIMER_CNTL_DIV16;
+       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);
 
-       setup_irq(IRQ_TIMER1, &footbridge_timer_irq);
+       clockevents_register_device(ce);
 }
 
 struct sys_timer footbridge_timer = {
        .init           = footbridge_timer_init,
-       .offset         = timer1_gettimeoffset,
 };
index f488fa2082d76c07e51c890bb81856cb69b1618f..441c6ce0d555afe96ff8eaf1ddd7815846b71b33 100644 (file)
@@ -4,10 +4,13 @@
  *  Copyright (C) 1998 Russell King.
  *  Copyright (C) 1998 Phil Blundell
  */
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/timex.h>
 
 #include <asm/irq.h>
 
 
 #include "common.h"
 
-/*
- * ISA timer tick support
- */
-#define mSEC_10_from_14 ((14318180 + 100) / 200)
+#define PIT_MODE       0x43
+#define PIT_CH0                0x40
+
+#define PIT_LATCH      ((PIT_TICK_RATE + HZ / 2) / HZ)
 
-static unsigned long isa_gettimeoffset(void)
+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;
+       return (cycle_t)(jifs * PIT_LATCH) + count;
+}
 
-       count_p = count;
+static struct clocksource pit_cs = {
+       .name           = "pit",
+       .rating         = 110,
+       .read           = pit_read,
+       .mask           = CLOCKSOURCE_MASK(32),
+};
 
-       count = (((mSEC_10_from_14/6)-1) - count) * (tick_nsec / 1000);
-       count = (count + (mSEC_10_from_14/6)/2) / (mSEC_10_from_14/6);
+static void pit_set_mode(enum clock_event_mode mode,
+       struct clock_event_device *evt)
+{
+       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);
+}
 
-       return count;
+static int pit_set_next_event(unsigned long delta,
+       struct clock_event_device *evt)
+{
+       return 0;
 }
 
-static irqreturn_t
-isa_timer_interrupt(int irq, void *dev_id)
+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)
 {
-       timer_tick();
+       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);
+
+       clocksource_register_hz(&pit_cs, PIT_TICK_RATE);
 
-       setup_irq(IRQ_ISA_TIMER, &isa_timer_irq);
+       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,
 };