diff options
Diffstat (limited to 'arch/arm/kernel/smp_twd.c')
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 47 |
1 files changed, 45 insertions, 2 deletions
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 01c186222f3b..a8a6682d6b52 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -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); } |