[MIPS] time: SMP-proofing of Sibyte clockevent/clocksource code.
[linux-2.6.git] / arch / mips / sibyte / sb1250 / time.c
index 9ef5462..a41e908 100644 (file)
 
 extern int sb1250_steal_irq(int irq);
 
-static cycle_t sb1250_hpt_read(void);
-
-void __init sb1250_hpt_setup(void)
-{
-       int cpu = smp_processor_id();
-
-       if (!cpu) {
-               /* Setup hpt using timer #3 but do not enable irq for it */
-               __raw_writeq(0, IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_CFG)));
-               __raw_writeq(SB1250_HPT_VALUE,
-                            IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_INIT)));
-               __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
-                            IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_CFG)));
-
-               mips_hpt_frequency = V_SCD_TIMER_FREQ;
-               clocksource_mips.read = sb1250_hpt_read;
-               clocksource_mips.mask = M_SCD_TIMER_INIT;
-       }
-}
-
 /*
  * The general purpose timer ticks at 1 Mhz independent if
  * the rest of the system
@@ -121,18 +101,14 @@ sibyte_next_event(unsigned long delta, struct clock_event_device *evt)
        return 0;
 }
 
-struct clock_event_device sibyte_hpt_clockevent = {
-       .name           = "sb1250-counter",
-       .features       = CLOCK_EVT_FEAT_PERIODIC,
-       .set_mode       = sibyte_set_mode,
-       .set_next_event = sibyte_next_event,
-       .shift          = 32,
-       .irq            = 0,
-};
-
 static irqreturn_t sibyte_counter_handler(int irq, void *dev_id)
 {
-       struct clock_event_device *cd = &sibyte_hpt_clockevent;
+       unsigned int cpu = smp_processor_id();
+       struct clock_event_device *cd = dev_id;
+
+       /* ACK interrupt */
+       ____raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
+                      IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
 
        cd->event_handler(cd);
 
@@ -145,15 +121,35 @@ static struct irqaction sibyte_irqaction = {
        .name           = "timer",
 };
 
+static DEFINE_PER_CPU(struct clock_event_device, sibyte_hpt_clockevent);
+static DEFINE_PER_CPU(struct irqaction, sibyte_hpt_irqaction);
+static DEFINE_PER_CPU(char [18], sibyte_hpt_name);
+
 void __cpuinit sb1250_clockevent_init(void)
 {
-       struct clock_event_device *cd = &sibyte_hpt_clockevent;
        unsigned int cpu = smp_processor_id();
-       int irq = K_INT_TIMER_0 + cpu;
+       unsigned int irq = K_INT_TIMER_0 + cpu;
+       struct irqaction *action = &per_cpu(sibyte_hpt_irqaction, cpu);
+       struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu);
+       unsigned char *name = per_cpu(sibyte_hpt_name, cpu);
 
        /* Only have 4 general purpose timers, and we use last one as hpt */
        BUG_ON(cpu > 2);
 
+       sprintf(name, "bcm1480-counter %d", cpu);
+       cd->name                = name;
+       cd->features            = CLOCK_EVT_FEAT_PERIODIC |
+                                 CLOCK_EVT_MODE_ONESHOT;
+       clockevent_set_clock(cd, V_SCD_TIMER_FREQ);
+       cd->max_delta_ns        = clockevent_delta2ns(0x7fffff, cd);
+       cd->min_delta_ns        = clockevent_delta2ns(1, cd);
+       cd->rating              = 200;
+       cd->irq                 = irq;
+       cd->cpumask             = cpumask_of_cpu(cpu);
+       cd->set_next_event      = sibyte_next_event;
+       cd->set_mode            = sibyte_set_mode;
+       clockevents_register_device(cd);
+
        sb1250_mask_irq(cpu, irq);
 
        /* Map the timer interrupt to ip[4] of this cpu */
@@ -165,17 +161,11 @@ void __cpuinit sb1250_clockevent_init(void)
        sb1250_unmask_irq(cpu, irq);
        sb1250_steal_irq(irq);
 
-       /*
-        * This interrupt is "special" in that it doesn't use the request_irq
-        * way to hook the irq line.  The timer interrupt is initialized early
-        * enough to make this a major pain, and it's also firing enough to
-        * warrant a bit of special case code.  sb1250_timer_interrupt is
-        * called directly from irq_handler.S when IP[4] is set during an
-        * interrupt
-        */
+       action->handler = sibyte_counter_handler;
+       action->flags   = IRQF_DISABLED | IRQF_PERCPU;
+       action->name    = name;
+       action->dev_id  = cd;
        setup_irq(irq, &sibyte_irqaction);
-
-       clockevents_register_device(cd);
 }
 
 /*
@@ -195,8 +185,7 @@ struct clocksource bcm1250_clocksource = {
        .name   = "MIPS",
        .rating = 200,
        .read   = sb1250_hpt_read,
-       .mask   = CLOCKSOURCE_MASK(32),
-       .shift  = 32,
+       .mask   = CLOCKSOURCE_MASK(23),
        .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
@@ -204,6 +193,17 @@ void __init sb1250_clocksource_init(void)
 {
        struct clocksource *cs = &bcm1250_clocksource;
 
+       /* Setup hpt using timer #3 but do not enable irq for it */
+       __raw_writeq(0,
+                    IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM,
+                                                R_SCD_TIMER_CFG)));
+       __raw_writeq(SB1250_HPT_VALUE,
+                    IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM,
+                                                R_SCD_TIMER_INIT)));
+       __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
+                    IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM,
+                                                R_SCD_TIMER_CFG)));
+
        clocksource_set_clock(cs, V_SCD_TIMER_FREQ);
        clocksource_register(cs);
 }