Merge branch 'next/deletion' of git://git.linaro.org/people/arnd/arm-soc
[pandora-kernel.git] / arch / arm / kernel / smp_twd.c
index 01c1862..a8a6682 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/io.h>
 
 #include <asm/smp_twd.h>
+#include <asm/localtimer.h>
 #include <asm/hardware/gic.h>
 
 /* set up by the platform code */
@@ -26,6 +27,8 @@ void __iomem *twd_base;
 
 static unsigned long twd_timer_rate;
 
+static struct clock_event_device __percpu **twd_evt;
+
 static void twd_set_mode(enum clock_event_mode mode,
                        struct clock_event_device *clk)
 {
@@ -80,6 +83,12 @@ int twd_timer_ack(void)
        return 0;
 }
 
+void twd_timer_stop(struct clock_event_device *clk)
+{
+       twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+       disable_percpu_irq(clk->irq);
+}
+
 static void __cpuinit twd_calibrate_rate(void)
 {
        unsigned long count;
@@ -119,11 +128,43 @@ static void __cpuinit twd_calibrate_rate(void)
        }
 }
 
+static irqreturn_t twd_handler(int irq, void *dev_id)
+{
+       struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
+
+       if (twd_timer_ack()) {
+               evt->event_handler(evt);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
 /*
  * Setup the local clock events for a CPU.
  */
 void __cpuinit twd_timer_setup(struct clock_event_device *clk)
 {
+       struct clock_event_device **this_cpu_clk;
+
+       if (!twd_evt) {
+               int err;
+
+               twd_evt = alloc_percpu(struct clock_event_device *);
+               if (!twd_evt) {
+                       pr_err("twd: can't allocate memory\n");
+                       return;
+               }
+
+               err = request_percpu_irq(clk->irq, twd_handler,
+                                        "twd", twd_evt);
+               if (err) {
+                       pr_err("twd: can't register interrupt %d (%d)\n",
+                              clk->irq, err);
+                       return;
+               }
+       }
+
        twd_calibrate_rate();
 
        clk->name = "local_timer";
@@ -137,8 +178,10 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
        clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
        clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
 
+       this_cpu_clk = __this_cpu_ptr(twd_evt);
+       *this_cpu_clk = clk;
+
        clockevents_register_device(clk);
 
-       /* Make sure our local interrupt controller has this enabled */
-       gic_enable_ppi(clk->irq);
+       enable_percpu_irq(clk->irq, 0);
 }