aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/include/asm/localtimer.h51
-rw-r--r--arch/arm/include/asm/smp.h40
-rw-r--r--arch/arm/kernel/smp.c56
-rw-r--r--arch/arm/mach-realview/localtimer.c70
-rw-r--r--arch/arm/mach-realview/platsmp.c14
6 files changed, 121 insertions, 111 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index e60ec54df334..ef4f860d5041 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -859,6 +859,7 @@ source "kernel/time/Kconfig"
859config SMP 859config SMP
860 bool "Symmetric Multi-Processing (EXPERIMENTAL)" 860 bool "Symmetric Multi-Processing (EXPERIMENTAL)"
861 depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP) 861 depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP)
862 depends on GENERIC_CLOCKEVENTS
862 select USE_GENERIC_SMP_HELPERS 863 select USE_GENERIC_SMP_HELPERS
863 help 864 help
864 This enables support for systems with more than one CPU. If you have 865 This enables support for systems with more than one CPU. If you have
diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h
new file mode 100644
index 000000000000..3f8c9ebb646c
--- /dev/null
+++ b/arch/arm/include/asm/localtimer.h
@@ -0,0 +1,51 @@
1/*
2 * arch/arm/include/asm/localtimer.h
3 *
4 * Copyright (C) 2004-2005 ARM Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#ifndef __ASM_ARM_LOCALTIMER_H
11#define __ASM_ARM_LOCALTIMER_H
12
13struct clock_event_device;
14
15/*
16 * Setup a per-cpu timer, whether it be a local timer or dummy broadcast
17 */
18void percpu_timer_setup(void);
19
20/*
21 * Called from assembly, this is the local timer IRQ handler
22 */
23asmlinkage void do_local_timer(struct pt_regs *);
24
25
26#ifdef CONFIG_LOCAL_TIMERS
27/*
28 * Platform provides this to acknowledge a local timer IRQ.
29 * Returns true if the local timer IRQ is to be processed.
30 */
31int local_timer_ack(void);
32
33/*
34 * Stop a local timer interrupt.
35 */
36void local_timer_stop(void);
37
38/*
39 * Setup a local timer interrupt for a CPU.
40 */
41void local_timer_setup(struct clock_event_device *);
42
43#else
44
45static inline void local_timer_stop(void)
46{
47}
48
49#endif
50
51#endif
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index 5995935338e1..608f2d533ff2 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -56,11 +56,6 @@ extern void smp_store_cpu_info(unsigned int cpuid);
56extern void smp_cross_call(const struct cpumask *mask); 56extern void smp_cross_call(const struct cpumask *mask);
57 57
58/* 58/*
59 * Broadcast a clock event to other CPUs.
60 */
61extern void smp_timer_broadcast(const struct cpumask *mask);
62
63/*
64 * Boot a secondary CPU, and assign it the specified idle task. 59 * Boot a secondary CPU, and assign it the specified idle task.
65 * This also gives us the initial stack to use for this CPU. 60 * This also gives us the initial stack to use for this CPU.
66 */ 61 */
@@ -101,43 +96,8 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
101#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask 96#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask
102 97
103/* 98/*
104 * Local timer interrupt handling function (can be IPI'ed).
105 */
106extern void local_timer_interrupt(void);
107
108#ifdef CONFIG_LOCAL_TIMERS
109
110/*
111 * Stop a local timer interrupt.
112 */
113extern void local_timer_stop(void);
114
115/*
116 * Platform provides this to acknowledge a local timer IRQ
117 */
118extern int local_timer_ack(void);
119
120#else
121
122static inline void local_timer_stop(void)
123{
124}
125
126#endif
127
128/*
129 * Setup a local timer interrupt for a CPU.
130 */
131extern void local_timer_setup(void);
132
133/*
134 * show local interrupt info 99 * show local interrupt info
135 */ 100 */
136extern void show_local_irqs(struct seq_file *); 101extern void show_local_irqs(struct seq_file *);
137 102
138/*
139 * Called from assembly, this is the local timer IRQ handler
140 */
141asmlinkage void do_local_timer(struct pt_regs *);
142
143#endif /* ifndef __ASM_ARM_SMP_H */ 103#endif /* ifndef __ASM_ARM_SMP_H */
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 6014dfd22af4..91130e218aef 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -22,6 +22,8 @@
22#include <linux/smp.h> 22#include <linux/smp.h>
23#include <linux/seq_file.h> 23#include <linux/seq_file.h>
24#include <linux/irq.h> 24#include <linux/irq.h>
25#include <linux/percpu.h>
26#include <linux/clockchips.h>
25 27
26#include <asm/atomic.h> 28#include <asm/atomic.h>
27#include <asm/cacheflush.h> 29#include <asm/cacheflush.h>
@@ -32,6 +34,7 @@
32#include <asm/processor.h> 34#include <asm/processor.h>
33#include <asm/tlbflush.h> 35#include <asm/tlbflush.h>
34#include <asm/ptrace.h> 36#include <asm/ptrace.h>
37#include <asm/localtimer.h>
35 38
36/* 39/*
37 * as from 2.5, kernels no longer have an init_tasks structure 40 * as from 2.5, kernels no longer have an init_tasks structure
@@ -274,9 +277,9 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
274 local_fiq_enable(); 277 local_fiq_enable();
275 278
276 /* 279 /*
277 * Setup local timer for this CPU. 280 * Setup the percpu timer for this CPU.
278 */ 281 */
279 local_timer_setup(); 282 percpu_timer_setup();
280 283
281 calibrate_delay(); 284 calibrate_delay();
282 285
@@ -383,10 +386,16 @@ void show_local_irqs(struct seq_file *p)
383 seq_putc(p, '\n'); 386 seq_putc(p, '\n');
384} 387}
385 388
389/*
390 * Timer (local or broadcast) support
391 */
392static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent);
393
386static void ipi_timer(void) 394static void ipi_timer(void)
387{ 395{
396 struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent);
388 irq_enter(); 397 irq_enter();
389 local_timer_interrupt(); 398 evt->event_handler(evt);
390 irq_exit(); 399 irq_exit();
391} 400}
392 401
@@ -405,6 +414,42 @@ asmlinkage void __exception do_local_timer(struct pt_regs *regs)
405} 414}
406#endif 415#endif
407 416
417#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
418static void smp_timer_broadcast(const struct cpumask *mask)
419{
420 send_ipi_message(mask, IPI_TIMER);
421}
422
423static void broadcast_timer_set_mode(enum clock_event_mode mode,
424 struct clock_event_device *evt)
425{
426}
427
428static void local_timer_setup(struct clock_event_device *evt)
429{
430 evt->name = "dummy_timer";
431 evt->features = CLOCK_EVT_FEAT_ONESHOT |
432 CLOCK_EVT_FEAT_PERIODIC |
433 CLOCK_EVT_FEAT_DUMMY;
434 evt->rating = 400;
435 evt->mult = 1;
436 evt->set_mode = broadcast_timer_set_mode;
437 evt->broadcast = smp_timer_broadcast;
438
439 clockevents_register_device(evt);
440}
441#endif
442
443void __cpuinit percpu_timer_setup(void)
444{
445 unsigned int cpu = smp_processor_id();
446 struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
447
448 evt->cpumask = cpumask_of(cpu);
449
450 local_timer_setup(evt);
451}
452
408static DEFINE_SPINLOCK(stop_lock); 453static DEFINE_SPINLOCK(stop_lock);
409 454
410/* 455/*
@@ -501,11 +546,6 @@ void smp_send_reschedule(int cpu)
501 send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); 546 send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
502} 547}
503 548
504void smp_timer_broadcast(const struct cpumask *mask)
505{
506 send_ipi_message(mask, IPI_TIMER);
507}
508
509void smp_send_stop(void) 549void smp_send_stop(void)
510{ 550{
511 cpumask_t mask = cpu_online_map; 551 cpumask_t mask = cpu_online_map;
diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c
index 1c01d13460f0..cd98e7acd94d 100644
--- a/arch/arm/mach-realview/localtimer.c
+++ b/arch/arm/mach-realview/localtimer.c
@@ -11,10 +11,8 @@
11#include <linux/init.h> 11#include <linux/init.h>
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/delay.h> 13#include <linux/delay.h>
14#include <linux/device.h>
15#include <linux/smp.h> 14#include <linux/smp.h>
16#include <linux/jiffies.h> 15#include <linux/jiffies.h>
17#include <linux/percpu.h>
18#include <linux/clockchips.h> 16#include <linux/clockchips.h>
19#include <linux/irq.h> 17#include <linux/irq.h>
20#include <linux/io.h> 18#include <linux/io.h>
@@ -24,18 +22,6 @@
24#include <mach/hardware.h> 22#include <mach/hardware.h>
25#include <asm/irq.h> 23#include <asm/irq.h>
26 24
27static DEFINE_PER_CPU(struct clock_event_device, local_clockevent);
28
29/*
30 * Used on SMP for either the local timer or IPI_TIMER
31 */
32void local_timer_interrupt(void)
33{
34 struct clock_event_device *clk = &__get_cpu_var(local_clockevent);
35
36 clk->event_handler(clk);
37}
38
39#ifdef CONFIG_LOCAL_TIMERS 25#ifdef CONFIG_LOCAL_TIMERS
40 26
41/* set up by the platform code */ 27/* set up by the platform code */
@@ -44,7 +30,7 @@ void __iomem *twd_base;
44static unsigned long mpcore_timer_rate; 30static unsigned long mpcore_timer_rate;
45 31
46static void local_timer_set_mode(enum clock_event_mode mode, 32static void local_timer_set_mode(enum clock_event_mode mode,
47 struct clock_event_device *clk) 33 struct clock_event_device *evt)
48{ 34{
49 unsigned long ctrl; 35 unsigned long ctrl;
50 36
@@ -140,32 +126,29 @@ static void __cpuinit twd_calibrate_rate(void)
140/* 126/*
141 * Setup the local clock events for a CPU. 127 * Setup the local clock events for a CPU.
142 */ 128 */
143void __cpuinit local_timer_setup(void) 129void __cpuinit local_timer_setup(struct clock_event_device *evt)
144{ 130{
145 unsigned int cpu = smp_processor_id();
146 struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
147 unsigned long flags; 131 unsigned long flags;
148 132
149 twd_calibrate_rate(); 133 twd_calibrate_rate();
150 134
151 clk->name = "local_timer"; 135 evt->name = "local_timer";
152 clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 136 evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
153 clk->rating = 350; 137 evt->rating = 350;
154 clk->set_mode = local_timer_set_mode; 138 evt->set_mode = local_timer_set_mode;
155 clk->set_next_event = local_timer_set_next_event; 139 evt->set_next_event = local_timer_set_next_event;
156 clk->irq = IRQ_LOCALTIMER; 140 evt->irq = IRQ_LOCALTIMER;
157 clk->cpumask = cpumask_of(cpu); 141 evt->shift = 20;
158 clk->shift = 20; 142 evt->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, evt->shift);
159 clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift); 143 evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt);
160 clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); 144 evt->min_delta_ns = clockevent_delta2ns(0xf, evt);
161 clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
162 145
163 /* Make sure our local interrupt controller has this enabled */ 146 /* Make sure our local interrupt controller has this enabled */
164 local_irq_save(flags); 147 local_irq_save(flags);
165 get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER); 148 get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
166 local_irq_restore(flags); 149 local_irq_restore(flags);
167 150
168 clockevents_register_device(clk); 151 clockevents_register_device(evt);
169} 152}
170 153
171/* 154/*
@@ -176,29 +159,4 @@ void __cpuexit local_timer_stop(void)
176 __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 159 __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
177} 160}
178 161
179#else /* CONFIG_LOCAL_TIMERS */ 162#endif /* CONFIG_LOCAL_TIMERS */
180
181static void dummy_timer_set_mode(enum clock_event_mode mode,
182 struct clock_event_device *clk)
183{
184}
185
186void __cpuinit local_timer_setup(void)
187{
188 unsigned int cpu = smp_processor_id();
189 struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
190
191 clk->name = "dummy_timer";
192 clk->features = CLOCK_EVT_FEAT_ONESHOT |
193 CLOCK_EVT_FEAT_PERIODIC |
194 CLOCK_EVT_FEAT_DUMMY;
195 clk->rating = 400;
196 clk->mult = 1;
197 clk->set_mode = dummy_timer_set_mode;
198 clk->broadcast = smp_timer_broadcast;
199 clk->cpumask = cpumask_of(cpu);
200
201 clockevents_register_device(clk);
202}
203
204#endif /* !CONFIG_LOCAL_TIMERS */
diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c
index 30a9c68591f6..b34d3a57ce93 100644
--- a/arch/arm/mach-realview/platsmp.c
+++ b/arch/arm/mach-realview/platsmp.c
@@ -19,6 +19,7 @@
19#include <asm/cacheflush.h> 19#include <asm/cacheflush.h>
20#include <mach/hardware.h> 20#include <mach/hardware.h>
21#include <asm/mach-types.h> 21#include <asm/mach-types.h>
22#include <asm/localtimer.h>
22 23
23#include <mach/board-eb.h> 24#include <mach/board-eb.h>
24#include <mach/board-pb11mp.h> 25#include <mach/board-pb11mp.h>
@@ -217,13 +218,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
217 if (max_cpus > ncores) 218 if (max_cpus > ncores)
218 max_cpus = ncores; 219 max_cpus = ncores;
219 220
220#if defined(CONFIG_LOCAL_TIMERS) || defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
221 /*
222 * Enable the local timer or broadcast device for the boot CPU.
223 */
224 local_timer_setup();
225#endif
226
227 /* 221 /*
228 * Initialise the present map, which describes the set of CPUs 222 * Initialise the present map, which describes the set of CPUs
229 * actually populated at the present time. 223 * actually populated at the present time.
@@ -239,6 +233,12 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
239 * WFI 233 * WFI
240 */ 234 */
241 if (max_cpus > 1) { 235 if (max_cpus > 1) {
236 /*
237 * Enable the local timer or broadcast device for the
238 * boot CPU, but only if we have more than one CPU.
239 */
240 percpu_timer_setup();
241
242 scu_enable(); 242 scu_enable();
243 poke_milo(); 243 poke_milo();
244 } 244 }