diff options
author | Daniel Hellstrom <daniel@gaisler.com> | 2011-05-01 20:08:54 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-05-16 16:07:44 -0400 |
commit | 55dd23eca666876e6028aa35d5e391cfced54871 (patch) | |
tree | 7f1006b68c8f04f4590c5442e5822b71d87e761d /arch/sparc | |
parent | ecbc42b70acbc6327adefe9635db93fcf62bf59d (diff) |
sparc32, sun4d: Implemented SMP IPIs support for SUN4D machines
The sun4d does not seem to have a distingstion between soft and hard
IRQs. When generating IPIs the generated IRQ looks like a hard IRQ,
this patch adds a "IPI check" in the sun4d irq trap handler at a
predefined IRQ number (SUN4D_IPI_IRQ). Before generating an IPI
a per-cpu memory structure is modified for the "IPI check" to
successfully detect a IPI request to a specific processor, the check
clears the IPI work requested.
All three IPIs (resched, single and cpu-mask) use the same IRQ
number.
The IPI IRQ should preferrably be on a separate IRQ and definitly
not shared with IRQ handlers requesting IRQ with IRQF_SHARED.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/kernel/irq.h | 6 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4d_irq.c | 9 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4d_smp.c | 79 |
3 files changed, 94 insertions, 0 deletions
diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h index a189c273f638..100b9c204e78 100644 --- a/arch/sparc/kernel/irq.h +++ b/arch/sparc/kernel/irq.h | |||
@@ -86,4 +86,10 @@ BTFIXUPDEF_CALL(void, set_irq_udt, int) | |||
86 | #define set_cpu_int(cpu,level) BTFIXUP_CALL(set_cpu_int)(cpu,level) | 86 | #define set_cpu_int(cpu,level) BTFIXUP_CALL(set_cpu_int)(cpu,level) |
87 | #define clear_cpu_int(cpu,level) BTFIXUP_CALL(clear_cpu_int)(cpu,level) | 87 | #define clear_cpu_int(cpu,level) BTFIXUP_CALL(clear_cpu_int)(cpu,level) |
88 | #define set_irq_udt(cpu) BTFIXUP_CALL(set_irq_udt)(cpu) | 88 | #define set_irq_udt(cpu) BTFIXUP_CALL(set_irq_udt)(cpu) |
89 | |||
90 | /* All SUN4D IPIs are sent on this IRQ, may be shared with hard IRQs */ | ||
91 | #define SUN4D_IPI_IRQ 14 | ||
92 | |||
93 | extern void sun4d_ipi_interrupt(void); | ||
94 | |||
89 | #endif | 95 | #endif |
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index b830914e32d3..a9ea60eb2c10 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c | |||
@@ -156,6 +156,15 @@ void sun4d_handler_irq(int pil, struct pt_regs *regs) | |||
156 | 156 | ||
157 | cc_set_iclr(1 << pil); | 157 | cc_set_iclr(1 << pil); |
158 | 158 | ||
159 | #ifdef CONFIG_SMP | ||
160 | /* | ||
161 | * Check IPI data structures after IRQ has been cleared. Hard and Soft | ||
162 | * IRQ can happen at the same time, so both cases are always handled. | ||
163 | */ | ||
164 | if (pil == SUN4D_IPI_IRQ) | ||
165 | sun4d_ipi_interrupt(); | ||
166 | #endif | ||
167 | |||
159 | old_regs = set_irq_regs(regs); | 168 | old_regs = set_irq_regs(regs); |
160 | irq_enter(); | 169 | irq_enter(); |
161 | if (sbusl == 0) { | 170 | if (sbusl == 0) { |
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 38423a05ae41..30ca6245692f 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c | |||
@@ -32,6 +32,7 @@ static inline unsigned long sun4d_swap(volatile unsigned long *ptr, unsigned lon | |||
32 | return val; | 32 | return val; |
33 | } | 33 | } |
34 | 34 | ||
35 | static void smp4d_ipi_init(void); | ||
35 | static void smp_setup_percpu_timer(void); | 36 | static void smp_setup_percpu_timer(void); |
36 | 37 | ||
37 | static unsigned char cpu_leds[32]; | 38 | static unsigned char cpu_leds[32]; |
@@ -118,6 +119,7 @@ void __cpuinit smp4d_callin(void) | |||
118 | */ | 119 | */ |
119 | void __init smp4d_boot_cpus(void) | 120 | void __init smp4d_boot_cpus(void) |
120 | { | 121 | { |
122 | smp4d_ipi_init(); | ||
121 | if (boot_cpu_id) | 123 | if (boot_cpu_id) |
122 | current_set[0] = NULL; | 124 | current_set[0] = NULL; |
123 | smp_setup_percpu_timer(); | 125 | smp_setup_percpu_timer(); |
@@ -189,6 +191,80 @@ void __init smp4d_smp_done(void) | |||
189 | sun4d_distribute_irqs(); | 191 | sun4d_distribute_irqs(); |
190 | } | 192 | } |
191 | 193 | ||
194 | /* Memory structure giving interrupt handler information about IPI generated */ | ||
195 | struct sun4d_ipi_work { | ||
196 | int single; | ||
197 | int msk; | ||
198 | int resched; | ||
199 | }; | ||
200 | |||
201 | static DEFINE_PER_CPU_SHARED_ALIGNED(struct sun4d_ipi_work, sun4d_ipi_work); | ||
202 | |||
203 | /* Initialize IPIs on the SUN4D SMP machine */ | ||
204 | static void __init smp4d_ipi_init(void) | ||
205 | { | ||
206 | int cpu; | ||
207 | struct sun4d_ipi_work *work; | ||
208 | |||
209 | printk(KERN_INFO "smp4d: setup IPI at IRQ %d\n", SUN4D_IPI_IRQ); | ||
210 | |||
211 | for_each_possible_cpu(cpu) { | ||
212 | work = &per_cpu(sun4d_ipi_work, cpu); | ||
213 | work->single = work->msk = work->resched = 0; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | void sun4d_ipi_interrupt(void) | ||
218 | { | ||
219 | struct sun4d_ipi_work *work = &__get_cpu_var(sun4d_ipi_work); | ||
220 | |||
221 | if (work->single) { | ||
222 | work->single = 0; | ||
223 | smp_call_function_single_interrupt(); | ||
224 | } | ||
225 | if (work->msk) { | ||
226 | work->msk = 0; | ||
227 | smp_call_function_interrupt(); | ||
228 | } | ||
229 | if (work->resched) { | ||
230 | work->resched = 0; | ||
231 | smp_resched_interrupt(); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | static void smp4d_ipi_single(int cpu) | ||
236 | { | ||
237 | struct sun4d_ipi_work *work = &per_cpu(sun4d_ipi_work, cpu); | ||
238 | |||
239 | /* Mark work */ | ||
240 | work->single = 1; | ||
241 | |||
242 | /* Generate IRQ on the CPU */ | ||
243 | sun4d_send_ipi(cpu, SUN4D_IPI_IRQ); | ||
244 | } | ||
245 | |||
246 | static void smp4d_ipi_mask_one(int cpu) | ||
247 | { | ||
248 | struct sun4d_ipi_work *work = &per_cpu(sun4d_ipi_work, cpu); | ||
249 | |||
250 | /* Mark work */ | ||
251 | work->msk = 1; | ||
252 | |||
253 | /* Generate IRQ on the CPU */ | ||
254 | sun4d_send_ipi(cpu, SUN4D_IPI_IRQ); | ||
255 | } | ||
256 | |||
257 | static void smp4d_ipi_resched(int cpu) | ||
258 | { | ||
259 | struct sun4d_ipi_work *work = &per_cpu(sun4d_ipi_work, cpu); | ||
260 | |||
261 | /* Mark work */ | ||
262 | work->resched = 1; | ||
263 | |||
264 | /* Generate IRQ on the CPU (any IRQ will cause resched) */ | ||
265 | sun4d_send_ipi(cpu, SUN4D_IPI_IRQ); | ||
266 | } | ||
267 | |||
192 | static struct smp_funcall { | 268 | static struct smp_funcall { |
193 | smpfunc_t func; | 269 | smpfunc_t func; |
194 | unsigned long arg1; | 270 | unsigned long arg1; |
@@ -354,6 +430,9 @@ void __init sun4d_init_smp(void) | |||
354 | BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current); | 430 | BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current); |
355 | BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM); | 431 | BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM); |
356 | BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM); | 432 | BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM); |
433 | BTFIXUPSET_CALL(smp_ipi_resched, smp4d_ipi_resched, BTFIXUPCALL_NORM); | ||
434 | BTFIXUPSET_CALL(smp_ipi_single, smp4d_ipi_single, BTFIXUPCALL_NORM); | ||
435 | BTFIXUPSET_CALL(smp_ipi_mask_one, smp4d_ipi_mask_one, BTFIXUPCALL_NORM); | ||
357 | 436 | ||
358 | for (i = 0; i < NR_CPUS; i++) { | 437 | for (i = 0; i < NR_CPUS; i++) { |
359 | ccall_info.processors_in[i] = 1; | 438 | ccall_info.processors_in[i] = 1; |