diff options
Diffstat (limited to 'arch/arm/kernel/smp_twd.c')
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 123 |
1 files changed, 100 insertions, 23 deletions
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 7a79b24597b2..fef42b21cecb 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c | |||
@@ -18,20 +18,23 @@ | |||
18 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
19 | #include <linux/jiffies.h> | 19 | #include <linux/jiffies.h> |
20 | #include <linux/clockchips.h> | 20 | #include <linux/clockchips.h> |
21 | #include <linux/irq.h> | 21 | #include <linux/interrupt.h> |
22 | #include <linux/io.h> | 22 | #include <linux/io.h> |
23 | #include <linux/of_irq.h> | ||
24 | #include <linux/of_address.h> | ||
23 | 25 | ||
24 | #include <asm/smp_twd.h> | 26 | #include <asm/smp_twd.h> |
25 | #include <asm/localtimer.h> | 27 | #include <asm/localtimer.h> |
26 | #include <asm/hardware/gic.h> | 28 | #include <asm/hardware/gic.h> |
27 | 29 | ||
28 | /* set up by the platform code */ | 30 | /* set up by the platform code */ |
29 | void __iomem *twd_base; | 31 | static void __iomem *twd_base; |
30 | 32 | ||
31 | static struct clk *twd_clk; | 33 | static struct clk *twd_clk; |
32 | static unsigned long twd_timer_rate; | 34 | static unsigned long twd_timer_rate; |
33 | 35 | ||
34 | static struct clock_event_device __percpu **twd_evt; | 36 | static struct clock_event_device __percpu **twd_evt; |
37 | static int twd_ppi; | ||
35 | 38 | ||
36 | static void twd_set_mode(enum clock_event_mode mode, | 39 | static void twd_set_mode(enum clock_event_mode mode, |
37 | struct clock_event_device *clk) | 40 | struct clock_event_device *clk) |
@@ -77,7 +80,7 @@ static int twd_set_next_event(unsigned long evt, | |||
77 | * If a local timer interrupt has occurred, acknowledge and return 1. | 80 | * If a local timer interrupt has occurred, acknowledge and return 1. |
78 | * Otherwise, return 0. | 81 | * Otherwise, return 0. |
79 | */ | 82 | */ |
80 | int twd_timer_ack(void) | 83 | static int twd_timer_ack(void) |
81 | { | 84 | { |
82 | if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { | 85 | if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { |
83 | __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); | 86 | __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); |
@@ -87,7 +90,7 @@ int twd_timer_ack(void) | |||
87 | return 0; | 90 | return 0; |
88 | } | 91 | } |
89 | 92 | ||
90 | void twd_timer_stop(struct clock_event_device *clk) | 93 | static void twd_timer_stop(struct clock_event_device *clk) |
91 | { | 94 | { |
92 | twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); | 95 | twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); |
93 | disable_percpu_irq(clk->irq); | 96 | disable_percpu_irq(clk->irq); |
@@ -222,28 +225,10 @@ static struct clk *twd_get_clock(void) | |||
222 | /* | 225 | /* |
223 | * Setup the local clock events for a CPU. | 226 | * Setup the local clock events for a CPU. |
224 | */ | 227 | */ |
225 | void __cpuinit twd_timer_setup(struct clock_event_device *clk) | 228 | static int __cpuinit twd_timer_setup(struct clock_event_device *clk) |
226 | { | 229 | { |
227 | struct clock_event_device **this_cpu_clk; | 230 | struct clock_event_device **this_cpu_clk; |
228 | 231 | ||
229 | if (!twd_evt) { | ||
230 | int err; | ||
231 | |||
232 | twd_evt = alloc_percpu(struct clock_event_device *); | ||
233 | if (!twd_evt) { | ||
234 | pr_err("twd: can't allocate memory\n"); | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | err = request_percpu_irq(clk->irq, twd_handler, | ||
239 | "twd", twd_evt); | ||
240 | if (err) { | ||
241 | pr_err("twd: can't register interrupt %d (%d)\n", | ||
242 | clk->irq, err); | ||
243 | return; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | if (!twd_clk) | 232 | if (!twd_clk) |
248 | twd_clk = twd_get_clock(); | 233 | twd_clk = twd_get_clock(); |
249 | 234 | ||
@@ -260,6 +245,7 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) | |||
260 | clk->rating = 350; | 245 | clk->rating = 350; |
261 | clk->set_mode = twd_set_mode; | 246 | clk->set_mode = twd_set_mode; |
262 | clk->set_next_event = twd_set_next_event; | 247 | clk->set_next_event = twd_set_next_event; |
248 | clk->irq = twd_ppi; | ||
263 | 249 | ||
264 | this_cpu_clk = __this_cpu_ptr(twd_evt); | 250 | this_cpu_clk = __this_cpu_ptr(twd_evt); |
265 | *this_cpu_clk = clk; | 251 | *this_cpu_clk = clk; |
@@ -267,4 +253,95 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) | |||
267 | clockevents_config_and_register(clk, twd_timer_rate, | 253 | clockevents_config_and_register(clk, twd_timer_rate, |
268 | 0xf, 0xffffffff); | 254 | 0xf, 0xffffffff); |
269 | enable_percpu_irq(clk->irq, 0); | 255 | enable_percpu_irq(clk->irq, 0); |
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static struct local_timer_ops twd_lt_ops __cpuinitdata = { | ||
261 | .setup = twd_timer_setup, | ||
262 | .stop = twd_timer_stop, | ||
263 | }; | ||
264 | |||
265 | static int __init twd_local_timer_common_register(void) | ||
266 | { | ||
267 | int err; | ||
268 | |||
269 | twd_evt = alloc_percpu(struct clock_event_device *); | ||
270 | if (!twd_evt) { | ||
271 | err = -ENOMEM; | ||
272 | goto out_free; | ||
273 | } | ||
274 | |||
275 | err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt); | ||
276 | if (err) { | ||
277 | pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err); | ||
278 | goto out_free; | ||
279 | } | ||
280 | |||
281 | err = local_timer_register(&twd_lt_ops); | ||
282 | if (err) | ||
283 | goto out_irq; | ||
284 | |||
285 | return 0; | ||
286 | |||
287 | out_irq: | ||
288 | free_percpu_irq(twd_ppi, twd_evt); | ||
289 | out_free: | ||
290 | iounmap(twd_base); | ||
291 | twd_base = NULL; | ||
292 | free_percpu(twd_evt); | ||
293 | |||
294 | return err; | ||
270 | } | 295 | } |
296 | |||
297 | int __init twd_local_timer_register(struct twd_local_timer *tlt) | ||
298 | { | ||
299 | if (twd_base || twd_evt) | ||
300 | return -EBUSY; | ||
301 | |||
302 | twd_ppi = tlt->res[1].start; | ||
303 | |||
304 | twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); | ||
305 | if (!twd_base) | ||
306 | return -ENOMEM; | ||
307 | |||
308 | return twd_local_timer_common_register(); | ||
309 | } | ||
310 | |||
311 | #ifdef CONFIG_OF | ||
312 | const static struct of_device_id twd_of_match[] __initconst = { | ||
313 | { .compatible = "arm,cortex-a9-twd-timer", }, | ||
314 | { .compatible = "arm,cortex-a5-twd-timer", }, | ||
315 | { .compatible = "arm,arm11mp-twd-timer", }, | ||
316 | { }, | ||
317 | }; | ||
318 | |||
319 | void __init twd_local_timer_of_register(void) | ||
320 | { | ||
321 | struct device_node *np; | ||
322 | int err; | ||
323 | |||
324 | np = of_find_matching_node(NULL, twd_of_match); | ||
325 | if (!np) { | ||
326 | err = -ENODEV; | ||
327 | goto out; | ||
328 | } | ||
329 | |||
330 | twd_ppi = irq_of_parse_and_map(np, 0); | ||
331 | if (!twd_ppi) { | ||
332 | err = -EINVAL; | ||
333 | goto out; | ||
334 | } | ||
335 | |||
336 | twd_base = of_iomap(np, 0); | ||
337 | if (!twd_base) { | ||
338 | err = -ENOMEM; | ||
339 | goto out; | ||
340 | } | ||
341 | |||
342 | err = twd_local_timer_common_register(); | ||
343 | |||
344 | out: | ||
345 | WARN(err, "twd_local_timer_of_register failed (%d)\n", err); | ||
346 | } | ||
347 | #endif | ||