Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / arch / arm / mach-footbridge / isa-timer.c
index f488fa2..441c6ce 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,
 };