1
2
3
4
5
6
7
8
9
10
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/sched.h>
15#include <linux/smp.h>
16#include <linux/interrupt.h>
17#include <linux/delay.h>
18#include <linux/init.h>
19#include <linux/spinlock.h>
20#include <linux/cpu.h>
21
22#include <asm/irq.h>
23#include <asm/smp.h>
24#include <asm/paca.h>
25#include <asm/machdep.h>
26#include <asm/cputable.h>
27#include <asm/firmware.h>
28#include <asm/rtas.h>
29#include <asm/vdso_datapage.h>
30#include <asm/cputhreads.h>
31#include <asm/xics.h>
32#include <asm/opal.h>
33
34#include "powernv.h"
35
36#ifdef DEBUG
37#include <asm/udbg.h>
38#define DBG(fmt...) udbg_printf(fmt)
39#else
40#define DBG(fmt...)
41#endif
42
43static void pnv_smp_setup_cpu(int cpu)
44{
45 if (cpu != boot_cpuid)
46 xics_setup_cpu();
47}
48
49int pnv_smp_kick_cpu(int nr)
50{
51 unsigned int pcpu = get_hard_smp_processor_id(nr);
52 unsigned long start_here = __pa(*((unsigned long *)
53 generic_secondary_smp_init));
54 long rc;
55
56 BUG_ON(nr < 0 || nr >= NR_CPUS);
57
58
59
60
61
62 if (paca[nr].cpu_start || !firmware_has_feature(FW_FEATURE_OPALv2))
63 goto kick;
64
65
66
67
68
69
70
71 if (firmware_has_feature(FW_FEATURE_OPALv3)) {
72 uint8_t status;
73
74 rc = opal_query_cpu_status(pcpu, &status);
75 if (rc != OPAL_SUCCESS) {
76 pr_warn("OPAL Error %ld querying CPU %d state\n",
77 rc, nr);
78 return -ENODEV;
79 }
80
81
82
83
84
85 if (status == OPAL_THREAD_STARTED)
86 goto kick;
87
88
89
90
91 if (status == OPAL_THREAD_INACTIVE) {
92 pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n",
93 nr, pcpu);
94 rc = opal_start_cpu(pcpu, start_here);
95 if (rc != OPAL_SUCCESS) {
96 pr_warn("OPAL Error %ld starting CPU %d\n",
97 rc, nr);
98 return -ENODEV;
99 }
100 } else {
101
102
103
104
105
106
107 pr_devel("OPAL: CPU %d (HW 0x%x) is unavailable"
108 " (status %d)...\n", nr, pcpu, status);
109 return -ENODEV;
110 }
111 } else {
112
113
114
115
116
117 opal_start_cpu(pcpu, start_here);
118 }
119 kick:
120 return smp_generic_kick_cpu(nr);
121}
122
123#ifdef CONFIG_HOTPLUG_CPU
124
125static int pnv_smp_cpu_disable(void)
126{
127 int cpu = smp_processor_id();
128
129
130
131
132
133 set_cpu_online(cpu, false);
134 vdso_data->processorCount--;
135 if (cpu == boot_cpuid)
136 boot_cpuid = cpumask_any(cpu_online_mask);
137 xics_migrate_irqs_away();
138 return 0;
139}
140
141static void pnv_smp_cpu_kill_self(void)
142{
143 unsigned int cpu;
144
145
146 local_irq_disable();
147 idle_task_exit();
148 current->active_mm = NULL;
149 cpu = smp_processor_id();
150 DBG("CPU%d offline\n", cpu);
151 generic_set_cpu_dead(cpu);
152 smp_wmb();
153
154
155
156
157 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
158 while (!generic_check_cpu_restart(cpu)) {
159 power7_nap();
160 if (!generic_check_cpu_restart(cpu)) {
161 DBG("CPU%d Unexpected exit while offline !\n", cpu);
162
163
164
165
166 local_irq_enable();
167 local_irq_disable();
168 }
169 }
170 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1);
171 DBG("CPU%d coming online...\n", cpu);
172}
173
174#endif
175
176static struct smp_ops_t pnv_smp_ops = {
177 .message_pass = smp_muxed_ipi_message_pass,
178 .cause_ipi = NULL,
179 .probe = xics_smp_probe,
180 .kick_cpu = pnv_smp_kick_cpu,
181 .setup_cpu = pnv_smp_setup_cpu,
182 .cpu_bootable = smp_generic_cpu_bootable,
183#ifdef CONFIG_HOTPLUG_CPU
184 .cpu_disable = pnv_smp_cpu_disable,
185 .cpu_die = generic_cpu_die,
186#endif
187};
188
189
190void __init pnv_smp_init(void)
191{
192 smp_ops = &pnv_smp_ops;
193
194
195
196
197
198#ifdef CONFIG_PPC_RTAS
199
200 if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
201 smp_ops->give_timebase = rtas_give_timebase;
202 smp_ops->take_timebase = rtas_take_timebase;
203 }
204#endif
205
206#ifdef CONFIG_HOTPLUG_CPU
207 ppc_md.cpu_die = pnv_smp_cpu_kill_self;
208#endif
209}
210