diff options
-rw-r--r-- | arch/powerpc/include/asm/dbell.h | 7 | ||||
-rw-r--r-- | arch/powerpc/kernel/dbell.c | 47 | ||||
-rw-r--r-- | arch/powerpc/platforms/85xx/smp.c | 4 |
3 files changed, 40 insertions, 18 deletions
diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h index 501189a543d1..ced7e487ca27 100644 --- a/arch/powerpc/include/asm/dbell.h +++ b/arch/powerpc/include/asm/dbell.h @@ -27,10 +27,9 @@ enum ppc_dbell { PPC_G_DBELL_MC = 4, /* guest mcheck doorbell */ }; -#ifdef CONFIG_SMP -extern unsigned long dbell_smp_message[NR_CPUS]; -extern void smp_dbell_message_pass(int target, int msg); -#endif +extern void doorbell_message_pass(int target, int msg); +extern void doorbell_exception(struct pt_regs *regs); +extern void doorbell_setup_this_cpu(void); static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag) { diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c index e3a717704fd6..1c7a94580c3f 100644 --- a/arch/powerpc/kernel/dbell.c +++ b/arch/powerpc/kernel/dbell.c @@ -13,45 +13,66 @@ #include <linux/kernel.h> #include <linux/smp.h> #include <linux/threads.h> +#include <linux/percpu.h> #include <asm/dbell.h> #ifdef CONFIG_SMP -unsigned long dbell_smp_message[NR_CPUS]; +struct doorbell_cpu_info { + unsigned long messages; /* current messages bits */ + unsigned int tag; /* tag value */ +}; -void smp_dbell_message_pass(int target, int msg) +static DEFINE_PER_CPU(struct doorbell_cpu_info, doorbell_cpu_info); + +void doorbell_setup_this_cpu(void) +{ + struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info); + + info->messages = 0; + info->tag = mfspr(SPRN_PIR) & 0x3fff; +} + +void doorbell_message_pass(int target, int msg) { + struct doorbell_cpu_info *info; int i; - if(target < NR_CPUS) { - set_bit(msg, &dbell_smp_message[target]); - ppc_msgsnd(PPC_DBELL, 0, target); + if (target < NR_CPUS) { + info = &per_cpu(doorbell_cpu_info, target); + set_bit(msg, &info->messages); + ppc_msgsnd(PPC_DBELL, 0, info->tag); } - else if(target == MSG_ALL_BUT_SELF) { + else if (target == MSG_ALL_BUT_SELF) { for_each_online_cpu(i) { if (i == smp_processor_id()) continue; - set_bit(msg, &dbell_smp_message[i]); - ppc_msgsnd(PPC_DBELL, 0, i); + info = &per_cpu(doorbell_cpu_info, i); + set_bit(msg, &info->messages); + ppc_msgsnd(PPC_DBELL, 0, info->tag); } } else { /* target == MSG_ALL */ - for_each_online_cpu(i) - set_bit(msg, &dbell_smp_message[i]); + for_each_online_cpu(i) { + info = &per_cpu(doorbell_cpu_info, i); + set_bit(msg, &info->messages); + } ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0); } } void doorbell_exception(struct pt_regs *regs) { - int cpu = smp_processor_id(); + struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info); int msg; - if (num_online_cpus() < 2) + /* Warning: regs can be NULL when called from irq enable */ + + if (!info->messages || (num_online_cpus() < 2)) return; for (msg = 0; msg < 4; msg++) - if (test_and_clear_bit(msg, &dbell_smp_message[cpu])) + if (test_and_clear_bit(msg, &info->messages)) smp_message_recv(msg); } diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index a15f582300d8..4c3cde911c71 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -99,6 +99,8 @@ static void __init smp_85xx_setup_cpu(int cpu_nr) { mpic_setup_this_cpu(); + if (cpu_has_feature(CPU_FTR_DBELL)) + doorbell_setup_this_cpu(); } struct smp_ops_t smp_85xx_ops = { @@ -117,7 +119,7 @@ void __init mpc85xx_smp_init(void) } if (cpu_has_feature(CPU_FTR_DBELL)) - smp_85xx_ops.message_pass = smp_dbell_message_pass; + smp_85xx_ops.message_pass = doorbell_message_pass; BUG_ON(!smp_85xx_ops.message_pass); |