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 __cpuinit pnv_smp_setup_cpu(int cpu)
44{
45 if (cpu != boot_cpuid)
46 xics_setup_cpu();
47}
48
49static int pnv_smp_cpu_bootable(unsigned int nr)
50{
51
52
53
54 if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) {
55 if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0)
56 return 0;
57 if (smt_enabled_at_boot
58 && cpu_thread_in_core(nr) >= smt_enabled_at_boot)
59 return 0;
60 }
61
62 return 1;
63}
64
65int pnv_smp_kick_cpu(int nr)
66{
67 unsigned int pcpu = get_hard_smp_processor_id(nr);
68 unsigned long start_here = __pa(*((unsigned long *)
69 generic_secondary_smp_init));
70 long rc;
71
72 BUG_ON(nr < 0 || nr >= NR_CPUS);
73
74
75
76
77
78 if (paca[nr].cpu_start || !firmware_has_feature(FW_FEATURE_OPALv2))
79 goto kick;
80
81
82
83
84
85
86
87 if (firmware_has_feature(FW_FEATURE_OPALv3)) {
88 uint8_t status;
89
90 rc = opal_query_cpu_status(pcpu, &status);
91 if (rc != OPAL_SUCCESS) {
92 pr_warn("OPAL Error %ld querying CPU %d state\n",
93 rc, nr);
94 return -ENODEV;
95 }
96
97
98
99
100
101 if (status == OPAL_THREAD_STARTED)
102 goto kick;
103
104
105
106
107 if (status == OPAL_THREAD_INACTIVE) {
108 pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n",
109 nr, pcpu);
110 rc = opal_start_cpu(pcpu, start_here);
111 if (rc != OPAL_SUCCESS) {
112 pr_warn("OPAL Error %ld starting CPU %d\n",
113 rc, nr);
114 return -ENODEV;
115 }
116 } else {
117
118
119
120
121
122
123 pr_devel("OPAL: CPU %d (HW 0x%x) is unavailable"
124 " (status %d)...\n", nr, pcpu, status);
125 return -ENODEV;
126 }
127 } else {
128
129
130
131
132
133 opal_start_cpu(pcpu, start_here);
134 }
135 kick:
136 return smp_generic_kick_cpu(nr);
137}
138
139#ifdef CONFIG_HOTPLUG_CPU
140
141static int pnv_smp_cpu_disable(void)
142{
143 int cpu = smp_processor_id();
144
145
146
147
148
149 set_cpu_online(cpu, false);
150 vdso_data->processorCount--;
151 if (cpu == boot_cpuid)
152 boot_cpuid = cpumask_any(cpu_online_mask);
153 xics_migrate_irqs_away();
154 return 0;
155}
156
157static void pnv_smp_cpu_kill_self(void)
158{
159 unsigned int cpu;
160
161
162 local_irq_disable();
163 idle_task_exit();
164 current->active_mm = NULL;
165 cpu = smp_processor_id();
166 DBG("CPU%d offline\n", cpu);
167 generic_set_cpu_dead(cpu);
168 smp_wmb();
169
170
171
172
173 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
174 while (!generic_check_cpu_restart(cpu)) {
175 power7_nap();
176 if (!generic_check_cpu_restart(cpu)) {
177 DBG("CPU%d Unexpected exit while offline !\n", cpu);
178
179
180
181
182 local_irq_enable();
183 local_irq_disable();
184 }
185 }
186 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1);
187 DBG("CPU%d coming online...\n", cpu);
188}
189
190#endif
191
192static struct smp_ops_t pnv_smp_ops = {
193 .message_pass = smp_muxed_ipi_message_pass,
194 .cause_ipi = NULL,
195 .probe = xics_smp_probe,
196 .kick_cpu = pnv_smp_kick_cpu,
197 .setup_cpu = pnv_smp_setup_cpu,
198 .cpu_bootable = pnv_smp_cpu_bootable,
199#ifdef CONFIG_HOTPLUG_CPU
200 .cpu_disable = pnv_smp_cpu_disable,
201 .cpu_die = generic_cpu_die,
202#endif
203};
204
205
206void __init pnv_smp_init(void)
207{
208 smp_ops = &pnv_smp_ops;
209
210
211
212
213
214#ifdef CONFIG_PPC_RTAS
215
216 if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
217 smp_ops->give_timebase = rtas_give_timebase;
218 smp_ops->take_timebase = rtas_take_timebase;
219 }
220#endif
221
222#ifdef CONFIG_HOTPLUG_CPU
223 ppc_md.cpu_die = pnv_smp_cpu_kill_self;
224#endif
225}
226