aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/dbell.c46
-rw-r--r--arch/powerpc/kernel/irq.c4
-rw-r--r--arch/powerpc/kernel/smp.c94
3 files changed, 73 insertions, 71 deletions
diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c
index e49b24c84133..2cc451aaaca7 100644
--- a/arch/powerpc/kernel/dbell.c
+++ b/arch/powerpc/kernel/dbell.c
@@ -13,65 +13,35 @@
13#include <linux/kernel.h> 13#include <linux/kernel.h>
14#include <linux/smp.h> 14#include <linux/smp.h>
15#include <linux/threads.h> 15#include <linux/threads.h>
16#include <linux/percpu.h> 16#include <linux/hardirq.h>
17 17
18#include <asm/dbell.h> 18#include <asm/dbell.h>
19#include <asm/irq_regs.h> 19#include <asm/irq_regs.h>
20 20
21#ifdef CONFIG_SMP 21#ifdef CONFIG_SMP
22struct doorbell_cpu_info {
23 unsigned long messages; /* current messages bits */
24 unsigned int tag; /* tag value */
25};
26
27static DEFINE_PER_CPU(struct doorbell_cpu_info, doorbell_cpu_info);
28
29void doorbell_setup_this_cpu(void) 22void doorbell_setup_this_cpu(void)
30{ 23{
31 struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info); 24 unsigned long tag = mfspr(SPRN_PIR) & 0x3fff;
32 25
33 info->messages = 0; 26 smp_muxed_ipi_set_data(smp_processor_id(), tag);
34 info->tag = mfspr(SPRN_PIR) & 0x3fff;
35} 27}
36 28
37void doorbell_message_pass(int cpu, int msg) 29void doorbell_cause_ipi(int cpu, unsigned long data)
38{ 30{
39 struct doorbell_cpu_info *info; 31 ppc_msgsnd(PPC_DBELL, 0, data);
40
41 info = &per_cpu(doorbell_cpu_info, cpu);
42 set_bit(msg, &info->messages);
43 ppc_msgsnd(PPC_DBELL, 0, info->tag);
44} 32}
45 33
46void doorbell_exception(struct pt_regs *regs) 34void doorbell_exception(struct pt_regs *regs)
47{ 35{
48 struct pt_regs *old_regs = set_irq_regs(regs); 36 struct pt_regs *old_regs = set_irq_regs(regs);
49 struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
50 int msg;
51 37
52 /* Warning: regs can be NULL when called from irq enable */ 38 irq_enter();
53 39
54 if (!info->messages || (num_online_cpus() < 2)) 40 smp_ipi_demux();
55 goto out;
56 41
57 for (msg = 0; msg < 4; msg++) 42 irq_exit();
58 if (test_and_clear_bit(msg, &info->messages))
59 smp_message_recv(msg);
60
61out:
62 set_irq_regs(old_regs); 43 set_irq_regs(old_regs);
63} 44}
64
65void doorbell_check_self(void)
66{
67 struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
68
69 if (!info->messages)
70 return;
71
72 ppc_msgsnd(PPC_DBELL, 0, info->tag);
73}
74
75#else /* CONFIG_SMP */ 45#else /* CONFIG_SMP */
76void doorbell_exception(struct pt_regs *regs) 46void doorbell_exception(struct pt_regs *regs)
77{ 47{
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index a81dd74414bf..826552cecebd 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -66,7 +66,6 @@
66#include <asm/ptrace.h> 66#include <asm/ptrace.h>
67#include <asm/machdep.h> 67#include <asm/machdep.h>
68#include <asm/udbg.h> 68#include <asm/udbg.h>
69#include <asm/dbell.h>
70#include <asm/smp.h> 69#include <asm/smp.h>
71 70
72#ifdef CONFIG_PPC64 71#ifdef CONFIG_PPC64
@@ -160,7 +159,8 @@ notrace void arch_local_irq_restore(unsigned long en)
160 159
161#if defined(CONFIG_BOOKE) && defined(CONFIG_SMP) 160#if defined(CONFIG_BOOKE) && defined(CONFIG_SMP)
162 /* Check for pending doorbell interrupts and resend to ourself */ 161 /* Check for pending doorbell interrupts and resend to ourself */
163 doorbell_check_self(); 162 if (cpu_has_feature(CPU_FTR_DBELL))
163 smp_muxed_ipi_resend();
164#endif 164#endif
165 165
166 /* 166 /*
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index b74411446922..fa8e8700064b 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -111,35 +111,6 @@ int __devinit smp_generic_kick_cpu(int nr)
111} 111}
112#endif 112#endif
113 113
114void smp_message_recv(int msg)
115{
116 switch(msg) {
117 case PPC_MSG_CALL_FUNCTION:
118 generic_smp_call_function_interrupt();
119 break;
120 case PPC_MSG_RESCHEDULE:
121 /* we notice need_resched on exit */
122 break;
123 case PPC_MSG_CALL_FUNC_SINGLE:
124 generic_smp_call_function_single_interrupt();
125 break;
126 case PPC_MSG_DEBUGGER_BREAK:
127 if (crash_ipi_function_ptr) {
128 crash_ipi_function_ptr(get_irq_regs());
129 break;
130 }
131#ifdef CONFIG_DEBUGGER
132 debugger_ipi(get_irq_regs());
133 break;
134#endif /* CONFIG_DEBUGGER */
135 /* FALLTHROUGH */
136 default:
137 printk("SMP %d: smp_message_recv(): unknown msg %d\n",
138 smp_processor_id(), msg);
139 break;
140 }
141}
142
143static irqreturn_t call_function_action(int irq, void *data) 114static irqreturn_t call_function_action(int irq, void *data)
144{ 115{
145 generic_smp_call_function_interrupt(); 116 generic_smp_call_function_interrupt();
@@ -158,9 +129,17 @@ static irqreturn_t call_function_single_action(int irq, void *data)
158 return IRQ_HANDLED; 129 return IRQ_HANDLED;
159} 130}
160 131
161static irqreturn_t debug_ipi_action(int irq, void *data) 132irqreturn_t debug_ipi_action(int irq, void *data)
162{ 133{
163 smp_message_recv(PPC_MSG_DEBUGGER_BREAK); 134 if (crash_ipi_function_ptr) {
135 crash_ipi_function_ptr(get_irq_regs());
136 return IRQ_HANDLED;
137 }
138
139#ifdef CONFIG_DEBUGGER
140 debugger_ipi(get_irq_regs());
141#endif /* CONFIG_DEBUGGER */
142
164 return IRQ_HANDLED; 143 return IRQ_HANDLED;
165} 144}
166 145
@@ -199,6 +178,59 @@ int smp_request_message_ipi(int virq, int msg)
199 return err; 178 return err;
200} 179}
201 180
181struct cpu_messages {
182 unsigned long messages; /* current messages bits */
183 unsigned long data; /* data for cause ipi */
184};
185static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_messages, ipi_message);
186
187void smp_muxed_ipi_set_data(int cpu, unsigned long data)
188{
189 struct cpu_messages *info = &per_cpu(ipi_message, cpu);
190
191 info->data = data;
192}
193
194void smp_muxed_ipi_message_pass(int cpu, int msg)
195{
196 struct cpu_messages *info = &per_cpu(ipi_message, cpu);
197 unsigned long *tgt = &info->messages;
198
199 set_bit(msg, tgt);
200 mb();
201 smp_ops->cause_ipi(cpu, info->data);
202}
203
204void smp_muxed_ipi_resend(void)
205{
206 struct cpu_messages *info = &__get_cpu_var(ipi_message);
207 unsigned long *tgt = &info->messages;
208
209 if (*tgt)
210 smp_ops->cause_ipi(smp_processor_id(), info->data);
211}
212
213irqreturn_t smp_ipi_demux(void)
214{
215 struct cpu_messages *info = &__get_cpu_var(ipi_message);
216 unsigned long *tgt = &info->messages;
217
218 mb(); /* order any irq clear */
219 while (*tgt) {
220 if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, tgt))
221 generic_smp_call_function_interrupt();
222 if (test_and_clear_bit(PPC_MSG_RESCHEDULE, tgt))
223 reschedule_action(0, NULL); /* upcoming sched hook */
224 if (test_and_clear_bit(PPC_MSG_CALL_FUNC_SINGLE, tgt))
225 generic_smp_call_function_single_interrupt();
226#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
227 if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK, tgt))
228 debug_ipi_action(0, NULL);
229#endif
230 }
231 return IRQ_HANDLED;
232}
233
202void smp_send_reschedule(int cpu) 234void smp_send_reschedule(int cpu)
203{ 235{
204 if (likely(smp_ops)) 236 if (likely(smp_ops))