diff options
Diffstat (limited to 'arch/powerpc/kernel/dbell.c')
-rw-r--r-- | arch/powerpc/kernel/dbell.c | 78 |
1 files changed, 67 insertions, 11 deletions
diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c index 1493734cd871..3307a52d797f 100644 --- a/arch/powerpc/kernel/dbell.c +++ b/arch/powerpc/kernel/dbell.c | |||
@@ -13,32 +13,88 @@ | |||
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 | 17 | ||
17 | #include <asm/dbell.h> | 18 | #include <asm/dbell.h> |
19 | #include <asm/irq_regs.h> | ||
18 | 20 | ||
19 | #ifdef CONFIG_SMP | 21 | #ifdef CONFIG_SMP |
20 | unsigned long dbell_smp_message[NR_CPUS]; | 22 | struct doorbell_cpu_info { |
23 | unsigned long messages; /* current messages bits */ | ||
24 | unsigned int tag; /* tag value */ | ||
25 | }; | ||
21 | 26 | ||
22 | void smp_dbell_message_pass(int target, int msg) | 27 | static DEFINE_PER_CPU(struct doorbell_cpu_info, doorbell_cpu_info); |
28 | |||
29 | void doorbell_setup_this_cpu(void) | ||
30 | { | ||
31 | struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info); | ||
32 | |||
33 | info->messages = 0; | ||
34 | info->tag = mfspr(SPRN_PIR) & 0x3fff; | ||
35 | } | ||
36 | |||
37 | void doorbell_message_pass(int target, int msg) | ||
23 | { | 38 | { |
39 | struct doorbell_cpu_info *info; | ||
24 | int i; | 40 | int i; |
25 | 41 | ||
26 | if(target < NR_CPUS) { | 42 | if (target < NR_CPUS) { |
27 | set_bit(msg, &dbell_smp_message[target]); | 43 | info = &per_cpu(doorbell_cpu_info, target); |
28 | ppc_msgsnd(PPC_DBELL, 0, target); | 44 | set_bit(msg, &info->messages); |
45 | ppc_msgsnd(PPC_DBELL, 0, info->tag); | ||
29 | } | 46 | } |
30 | else if(target == MSG_ALL_BUT_SELF) { | 47 | else if (target == MSG_ALL_BUT_SELF) { |
31 | for_each_online_cpu(i) { | 48 | for_each_online_cpu(i) { |
32 | if (i == smp_processor_id()) | 49 | if (i == smp_processor_id()) |
33 | continue; | 50 | continue; |
34 | set_bit(msg, &dbell_smp_message[i]); | 51 | info = &per_cpu(doorbell_cpu_info, i); |
35 | ppc_msgsnd(PPC_DBELL, 0, i); | 52 | set_bit(msg, &info->messages); |
53 | ppc_msgsnd(PPC_DBELL, 0, info->tag); | ||
36 | } | 54 | } |
37 | } | 55 | } |
38 | else { /* target == MSG_ALL */ | 56 | else { /* target == MSG_ALL */ |
39 | for_each_online_cpu(i) | 57 | for_each_online_cpu(i) { |
40 | set_bit(msg, &dbell_smp_message[i]); | 58 | info = &per_cpu(doorbell_cpu_info, i); |
59 | set_bit(msg, &info->messages); | ||
60 | } | ||
41 | ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0); | 61 | ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0); |
42 | } | 62 | } |
43 | } | 63 | } |
44 | #endif | 64 | |
65 | void doorbell_exception(struct pt_regs *regs) | ||
66 | { | ||
67 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
68 | struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info); | ||
69 | int msg; | ||
70 | |||
71 | /* Warning: regs can be NULL when called from irq enable */ | ||
72 | |||
73 | if (!info->messages || (num_online_cpus() < 2)) | ||
74 | goto out; | ||
75 | |||
76 | for (msg = 0; msg < 4; msg++) | ||
77 | if (test_and_clear_bit(msg, &info->messages)) | ||
78 | smp_message_recv(msg); | ||
79 | |||
80 | out: | ||
81 | set_irq_regs(old_regs); | ||
82 | } | ||
83 | |||
84 | void doorbell_check_self(void) | ||
85 | { | ||
86 | struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info); | ||
87 | |||
88 | if (!info->messages) | ||
89 | return; | ||
90 | |||
91 | ppc_msgsnd(PPC_DBELL, 0, info->tag); | ||
92 | } | ||
93 | |||
94 | #else /* CONFIG_SMP */ | ||
95 | void doorbell_exception(struct pt_regs *regs) | ||
96 | { | ||
97 | printk(KERN_WARNING "Received doorbell on non-smp system\n"); | ||
98 | } | ||
99 | #endif /* CONFIG_SMP */ | ||
100 | |||