aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/smp_twd.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel/smp_twd.c')
-rw-r--r--arch/arm/kernel/smp_twd.c47
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 @@
19#include <linux/io.h> 19#include <linux/io.h>
20 20
21#include <asm/smp_twd.h> 21#include <asm/smp_twd.h>
22#include <asm/localtimer.h>
22#include <asm/hardware/gic.h> 23#include <asm/hardware/gic.h>
23 24
24/* set up by the platform code */ 25/* set up by the platform code */
@@ -26,6 +27,8 @@ void __iomem *twd_base;
26 27
27static unsigned long twd_timer_rate; 28static unsigned long twd_timer_rate;
28 29
30static struct clock_event_device __percpu **twd_evt;
31
29static void twd_set_mode(enum clock_event_mode mode, 32static void twd_set_mode(enum clock_event_mode mode,
30 struct clock_event_device *clk) 33 struct clock_event_device *clk)
31{ 34{
@@ -80,6 +83,12 @@ int twd_timer_ack(void)
80 return 0; 83 return 0;
81} 84}
82 85
86void twd_timer_stop(struct clock_event_device *clk)
87{
88 twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
89 disable_percpu_irq(clk->irq);
90}
91
83static void __cpuinit twd_calibrate_rate(void) 92static void __cpuinit twd_calibrate_rate(void)
84{ 93{
85 unsigned long count; 94 unsigned long count;
@@ -119,11 +128,43 @@ static void __cpuinit twd_calibrate_rate(void)
119 } 128 }
120} 129}
121 130
131static irqreturn_t twd_handler(int irq, void *dev_id)
132{
133 struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
134
135 if (twd_timer_ack()) {
136 evt->event_handler(evt);
137 return IRQ_HANDLED;
138 }
139
140 return IRQ_NONE;
141}
142
122/* 143/*
123 * Setup the local clock events for a CPU. 144 * Setup the local clock events for a CPU.
124 */ 145 */
125void __cpuinit twd_timer_setup(struct clock_event_device *clk) 146void __cpuinit twd_timer_setup(struct clock_event_device *clk)
126{ 147{
148 struct clock_event_device **this_cpu_clk;
149
150 if (!twd_evt) {
151 int err;
152
153 twd_evt = alloc_percpu(struct clock_event_device *);
154 if (!twd_evt) {
155 pr_err("twd: can't allocate memory\n");
156 return;
157 }
158
159 err = request_percpu_irq(clk->irq, twd_handler,
160 "twd", twd_evt);
161 if (err) {
162 pr_err("twd: can't register interrupt %d (%d)\n",
163 clk->irq, err);
164 return;
165 }
166 }
167
127 twd_calibrate_rate(); 168 twd_calibrate_rate();
128 169
129 clk->name = "local_timer"; 170 clk->name = "local_timer";
@@ -137,8 +178,10 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
137 clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); 178 clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
138 clk->min_delta_ns = clockevent_delta2ns(0xf, clk); 179 clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
139 180
181 this_cpu_clk = __this_cpu_ptr(twd_evt);
182 *this_cpu_clk = clk;
183
140 clockevents_register_device(clk); 184 clockevents_register_device(clk);
141 185
142 /* Make sure our local interrupt controller has this enabled */ 186 enable_percpu_irq(clk->irq, 0);
143 gic_enable_ppi(clk->irq);
144} 187}