aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/include/asm/reg.h9
-rw-r--r--arch/powerpc/platforms/powernv/Makefile2
-rw-r--r--arch/powerpc/platforms/powernv/powernv.h2
-rw-r--r--arch/powerpc/platforms/powernv/smp.c18
-rw-r--r--arch/powerpc/platforms/powernv/subcore-asm.S95
-rw-r--r--arch/powerpc/platforms/powernv/subcore.c392
-rw-r--r--arch/powerpc/platforms/powernv/subcore.h18
7 files changed, 527 insertions, 9 deletions
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 29de0152878f..2cd799b382ec 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -225,6 +225,7 @@
225#define CTRL_TE 0x00c00000 /* thread enable */ 225#define CTRL_TE 0x00c00000 /* thread enable */
226#define CTRL_RUNLATCH 0x1 226#define CTRL_RUNLATCH 0x1
227#define SPRN_DAWR 0xB4 227#define SPRN_DAWR 0xB4
228#define SPRN_RPR 0xBA /* Relative Priority Register */
228#define SPRN_CIABR 0xBB 229#define SPRN_CIABR 0xBB
229#define CIABR_PRIV 0x3 230#define CIABR_PRIV 0x3
230#define CIABR_PRIV_USER 1 231#define CIABR_PRIV_USER 1
@@ -273,8 +274,10 @@
273#define SPRN_HSRR1 0x13B /* Hypervisor Save/Restore 1 */ 274#define SPRN_HSRR1 0x13B /* Hypervisor Save/Restore 1 */
274#define SPRN_IC 0x350 /* Virtual Instruction Count */ 275#define SPRN_IC 0x350 /* Virtual Instruction Count */
275#define SPRN_VTB 0x351 /* Virtual Time Base */ 276#define SPRN_VTB 0x351 /* Virtual Time Base */
277#define SPRN_LDBAR 0x352 /* LD Base Address Register */
276#define SPRN_PMICR 0x354 /* Power Management Idle Control Reg */ 278#define SPRN_PMICR 0x354 /* Power Management Idle Control Reg */
277#define SPRN_PMSR 0x355 /* Power Management Status Reg */ 279#define SPRN_PMSR 0x355 /* Power Management Status Reg */
280#define SPRN_PMMAR 0x356 /* Power Management Memory Activity Register */
278#define SPRN_PMCR 0x374 /* Power Management Control Register */ 281#define SPRN_PMCR 0x374 /* Power Management Control Register */
279 282
280/* HFSCR and FSCR bit numbers are the same */ 283/* HFSCR and FSCR bit numbers are the same */
@@ -434,6 +437,12 @@
434#define HID0_BTCD (1<<1) /* Branch target cache disable */ 437#define HID0_BTCD (1<<1) /* Branch target cache disable */
435#define HID0_NOPDST (1<<1) /* No-op dst, dstt, etc. instr. */ 438#define HID0_NOPDST (1<<1) /* No-op dst, dstt, etc. instr. */
436#define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */ 439#define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */
440/* POWER8 HID0 bits */
441#define HID0_POWER8_4LPARMODE __MASK(61)
442#define HID0_POWER8_2LPARMODE __MASK(57)
443#define HID0_POWER8_1TO2LPAR __MASK(52)
444#define HID0_POWER8_1TO4LPAR __MASK(51)
445#define HID0_POWER8_DYNLPARDIS __MASK(48)
437 446
438#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */ 447#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */
439#ifdef CONFIG_6xx 448#ifdef CONFIG_6xx
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 63cebb9b4d45..4ad0d345bc96 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,7 +1,7 @@
1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o 1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o
2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o 2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
3obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o 3obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
4obj-y += opal-msglog.o 4obj-y += opal-msglog.o subcore.o subcore-asm.o
5 5
6obj-$(CONFIG_SMP) += smp.o 6obj-$(CONFIG_SMP) += smp.o
7obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o 7obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h
index 0051e108ef0f..75501bfede7f 100644
--- a/arch/powerpc/platforms/powernv/powernv.h
+++ b/arch/powerpc/platforms/powernv/powernv.h
@@ -25,4 +25,6 @@ static inline int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
25 25
26extern void pnv_lpc_init(void); 26extern void pnv_lpc_init(void);
27 27
28bool cpu_core_split_required(void);
29
28#endif /* _POWERNV_H */ 30#endif /* _POWERNV_H */
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 65faf998fe2c..0062a43a2e0d 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -161,15 +161,17 @@ static void pnv_smp_cpu_kill_self(void)
161 ppc64_runlatch_off(); 161 ppc64_runlatch_off();
162 power7_nap(1); 162 power7_nap(1);
163 ppc64_runlatch_on(); 163 ppc64_runlatch_on();
164 if (!generic_check_cpu_restart(cpu)) { 164
165 /* Reenable IRQs briefly to clear the IPI that woke us */
166 local_irq_enable();
167 local_irq_disable();
168 mb();
169
170 if (cpu_core_split_required())
171 continue;
172
173 if (!generic_check_cpu_restart(cpu))
165 DBG("CPU%d Unexpected exit while offline !\n", cpu); 174 DBG("CPU%d Unexpected exit while offline !\n", cpu);
166 /* We may be getting an IPI, so we re-enable
167 * interrupts to process it, it will be ignored
168 * since we aren't online (hopefully)
169 */
170 local_irq_enable();
171 local_irq_disable();
172 }
173 } 175 }
174 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); 176 mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1);
175 DBG("CPU%d coming online...\n", cpu); 177 DBG("CPU%d coming online...\n", cpu);
diff --git a/arch/powerpc/platforms/powernv/subcore-asm.S b/arch/powerpc/platforms/powernv/subcore-asm.S
new file mode 100644
index 000000000000..39bb24aa8f34
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/subcore-asm.S
@@ -0,0 +1,95 @@
1/*
2 * Copyright 2013, Michael (Ellerman|Neuling), IBM Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <asm/asm-offsets.h>
11#include <asm/ppc_asm.h>
12#include <asm/reg.h>
13
14#include "subcore.h"
15
16
17_GLOBAL(split_core_secondary_loop)
18 /*
19 * r3 = u8 *state, used throughout the routine
20 * r4 = temp
21 * r5 = temp
22 * ..
23 * r12 = MSR
24 */
25 mfmsr r12
26
27 /* Disable interrupts so SRR0/1 don't get trashed */
28 li r4,0
29 ori r4,r4,MSR_EE|MSR_SE|MSR_BE|MSR_RI
30 andc r4,r12,r4
31 sync
32 mtmsrd r4
33
34 /* Switch to real mode and leave interrupts off */
35 li r5, MSR_IR|MSR_DR
36 andc r5, r4, r5
37
38 LOAD_REG_ADDR(r4, real_mode)
39
40 mtspr SPRN_SRR0,r4
41 mtspr SPRN_SRR1,r5
42 rfid
43 b . /* prevent speculative execution */
44
45real_mode:
46 /* Grab values from unsplit SPRs */
47 mfspr r6, SPRN_LDBAR
48 mfspr r7, SPRN_PMMAR
49 mfspr r8, SPRN_PMCR
50 mfspr r9, SPRN_RPR
51 mfspr r10, SPRN_SDR1
52
53 /* Order reading the SPRs vs telling the primary we are ready to split */
54 sync
55
56 /* Tell thread 0 we are in real mode */
57 li r4, SYNC_STEP_REAL_MODE
58 stb r4, 0(r3)
59
60 li r5, (HID0_POWER8_4LPARMODE | HID0_POWER8_2LPARMODE)@highest
61 sldi r5, r5, 48
62
63 /* Loop until we see the split happen in HID0 */
641: mfspr r4, SPRN_HID0
65 and. r4, r4, r5
66 beq 1b
67
68 /*
69 * We only need to initialise the below regs once for each subcore,
70 * but it's simpler and harmless to do it on each thread.
71 */
72
73 /* Make sure various SPRS have sane values */
74 li r4, 0
75 mtspr SPRN_LPID, r4
76 mtspr SPRN_PCR, r4
77 mtspr SPRN_HDEC, r4
78
79 /* Restore SPR values now we are split */
80 mtspr SPRN_LDBAR, r6
81 mtspr SPRN_PMMAR, r7
82 mtspr SPRN_PMCR, r8
83 mtspr SPRN_RPR, r9
84 mtspr SPRN_SDR1, r10
85
86 LOAD_REG_ADDR(r5, virtual_mode)
87
88 /* Get out of real mode */
89 mtspr SPRN_SRR0,r5
90 mtspr SPRN_SRR1,r12
91 rfid
92 b . /* prevent speculative execution */
93
94virtual_mode:
95 blr
diff --git a/arch/powerpc/platforms/powernv/subcore.c b/arch/powerpc/platforms/powernv/subcore.c
new file mode 100644
index 000000000000..894ecb3eb596
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/subcore.c
@@ -0,0 +1,392 @@
1/*
2 * Copyright 2013, Michael (Ellerman|Neuling), IBM Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#define pr_fmt(fmt) "powernv: " fmt
11
12#include <linux/kernel.h>
13#include <linux/cpu.h>
14#include <linux/cpumask.h>
15#include <linux/device.h>
16#include <linux/gfp.h>
17#include <linux/smp.h>
18#include <linux/stop_machine.h>
19
20#include <asm/cputhreads.h>
21#include <asm/kvm_ppc.h>
22#include <asm/machdep.h>
23#include <asm/opal.h>
24#include <asm/smp.h>
25
26#include "subcore.h"
27
28
29/*
30 * Split/unsplit procedure:
31 *
32 * A core can be in one of three states, unsplit, 2-way split, and 4-way split.
33 *
34 * The mapping to subcores_per_core is simple:
35 *
36 * State | subcores_per_core
37 * ------------|------------------
38 * Unsplit | 1
39 * 2-way split | 2
40 * 4-way split | 4
41 *
42 * The core is split along thread boundaries, the mapping between subcores and
43 * threads is as follows:
44 *
45 * Unsplit:
46 * ----------------------------
47 * Subcore | 0 |
48 * ----------------------------
49 * Thread | 0 1 2 3 4 5 6 7 |
50 * ----------------------------
51 *
52 * 2-way split:
53 * -------------------------------------
54 * Subcore | 0 | 1 |
55 * -------------------------------------
56 * Thread | 0 1 2 3 | 4 5 6 7 |
57 * -------------------------------------
58 *
59 * 4-way split:
60 * -----------------------------------------
61 * Subcore | 0 | 1 | 2 | 3 |
62 * -----------------------------------------
63 * Thread | 0 1 | 2 3 | 4 5 | 6 7 |
64 * -----------------------------------------
65 *
66 *
67 * Transitions
68 * -----------
69 *
70 * It is not possible to transition between either of the split states, the
71 * core must first be unsplit. The legal transitions are:
72 *
73 * ----------- ---------------
74 * | | <----> | 2-way split |
75 * | | ---------------
76 * | Unsplit |
77 * | | ---------------
78 * | | <----> | 4-way split |
79 * ----------- ---------------
80 *
81 * Unsplitting
82 * -----------
83 *
84 * Unsplitting is the simpler procedure. It requires thread 0 to request the
85 * unsplit while all other threads NAP.
86 *
87 * Thread 0 clears HID0_POWER8_DYNLPARDIS (Dynamic LPAR Disable). This tells
88 * the hardware that if all threads except 0 are napping, the hardware should
89 * unsplit the core.
90 *
91 * Non-zero threads are sent to a NAP loop, they don't exit the loop until they
92 * see the core unsplit.
93 *
94 * Core 0 spins waiting for the hardware to see all the other threads napping
95 * and perform the unsplit.
96 *
97 * Once thread 0 sees the unsplit, it IPIs the secondary threads to wake them
98 * out of NAP. They will then see the core unsplit and exit the NAP loop.
99 *
100 * Splitting
101 * ---------
102 *
103 * The basic splitting procedure is fairly straight forward. However it is
104 * complicated by the fact that after the split occurs, the newly created
105 * subcores are not in a fully initialised state.
106 *
107 * Most notably the subcores do not have the correct value for SDR1, which
108 * means they must not be running in virtual mode when the split occurs. The
109 * subcores have separate timebases SPRs but these are pre-synchronised by
110 * opal.
111 *
112 * To begin with secondary threads are sent to an assembly routine. There they
113 * switch to real mode, so they are immune to the uninitialised SDR1 value.
114 * Once in real mode they indicate that they are in real mode, and spin waiting
115 * to see the core split.
116 *
117 * Thread 0 waits to see that all secondaries are in real mode, and then begins
118 * the splitting procedure. It firstly sets HID0_POWER8_DYNLPARDIS, which
119 * prevents the hardware from unsplitting. Then it sets the appropriate HID bit
120 * to request the split, and spins waiting to see that the split has happened.
121 *
122 * Concurrently the secondaries will notice the split. When they do they set up
123 * their SPRs, notably SDR1, and then they can return to virtual mode and exit
124 * the procedure.
125 */
126
127/* Initialised at boot by subcore_init() */
128static int subcores_per_core;
129
130/*
131 * Used to communicate to offline cpus that we want them to pop out of the
132 * offline loop and do a split or unsplit.
133 *
134 * 0 - no split happening
135 * 1 - unsplit in progress
136 * 2 - split to 2 in progress
137 * 4 - split to 4 in progress
138 */
139static int new_split_mode;
140
141static cpumask_var_t cpu_offline_mask;
142
143struct split_state {
144 u8 step;
145 u8 master;
146};
147
148static DEFINE_PER_CPU(struct split_state, split_state);
149
150static void wait_for_sync_step(int step)
151{
152 int i, cpu = smp_processor_id();
153
154 for (i = cpu + 1; i < cpu + threads_per_core; i++)
155 while(per_cpu(split_state, i).step < step)
156 barrier();
157
158 /* Order the wait loop vs any subsequent loads/stores. */
159 mb();
160}
161
162static void unsplit_core(void)
163{
164 u64 hid0, mask;
165 int i, cpu;
166
167 mask = HID0_POWER8_2LPARMODE | HID0_POWER8_4LPARMODE;
168
169 cpu = smp_processor_id();
170 if (cpu_thread_in_core(cpu) != 0) {
171 while (mfspr(SPRN_HID0) & mask)
172 power7_nap(0);
173
174 per_cpu(split_state, cpu).step = SYNC_STEP_UNSPLIT;
175 return;
176 }
177
178 hid0 = mfspr(SPRN_HID0);
179 hid0 &= ~HID0_POWER8_DYNLPARDIS;
180 mtspr(SPRN_HID0, hid0);
181
182 while (mfspr(SPRN_HID0) & mask)
183 cpu_relax();
184
185 /* Wake secondaries out of NAP */
186 for (i = cpu + 1; i < cpu + threads_per_core; i++)
187 smp_send_reschedule(i);
188
189 wait_for_sync_step(SYNC_STEP_UNSPLIT);
190}
191
192static void split_core(int new_mode)
193{
194 struct { u64 value; u64 mask; } split_parms[2] = {
195 { HID0_POWER8_1TO2LPAR, HID0_POWER8_2LPARMODE },
196 { HID0_POWER8_1TO4LPAR, HID0_POWER8_4LPARMODE }
197 };
198 int i, cpu;
199 u64 hid0;
200
201 /* Convert new_mode (2 or 4) into an index into our parms array */
202 i = (new_mode >> 1) - 1;
203 BUG_ON(i < 0 || i > 1);
204
205 cpu = smp_processor_id();
206 if (cpu_thread_in_core(cpu) != 0) {
207 split_core_secondary_loop(&per_cpu(split_state, cpu).step);
208 return;
209 }
210
211 wait_for_sync_step(SYNC_STEP_REAL_MODE);
212
213 /* Write new mode */
214 hid0 = mfspr(SPRN_HID0);
215 hid0 |= HID0_POWER8_DYNLPARDIS | split_parms[i].value;
216 mtspr(SPRN_HID0, hid0);
217
218 /* Wait for it to happen */
219 while (!(mfspr(SPRN_HID0) & split_parms[i].mask))
220 cpu_relax();
221}
222
223static void cpu_do_split(int new_mode)
224{
225 /*
226 * At boot subcores_per_core will be 0, so we will always unsplit at
227 * boot. In the usual case where the core is already unsplit it's a
228 * nop, and this just ensures the kernel's notion of the mode is
229 * consistent with the hardware.
230 */
231 if (subcores_per_core != 1)
232 unsplit_core();
233
234 if (new_mode != 1)
235 split_core(new_mode);
236
237 mb();
238 per_cpu(split_state, smp_processor_id()).step = SYNC_STEP_FINISHED;
239}
240
241bool cpu_core_split_required(void)
242{
243 smp_rmb();
244
245 if (!new_split_mode)
246 return false;
247
248 cpu_do_split(new_split_mode);
249
250 return true;
251}
252
253static int cpu_update_split_mode(void *data)
254{
255 int cpu, new_mode = *(int *)data;
256
257 if (this_cpu_ptr(&split_state)->master) {
258 new_split_mode = new_mode;
259 smp_wmb();
260
261 cpumask_andnot(cpu_offline_mask, cpu_present_mask,
262 cpu_online_mask);
263
264 /* This should work even though the cpu is offline */
265 for_each_cpu(cpu, cpu_offline_mask)
266 smp_send_reschedule(cpu);
267 }
268
269 cpu_do_split(new_mode);
270
271 if (this_cpu_ptr(&split_state)->master) {
272 /* Wait for all cpus to finish before we touch subcores_per_core */
273 for_each_present_cpu(cpu) {
274 if (cpu >= setup_max_cpus)
275 break;
276
277 while(per_cpu(split_state, cpu).step < SYNC_STEP_FINISHED)
278 barrier();
279 }
280
281 new_split_mode = 0;
282
283 /* Make the new mode public */
284 subcores_per_core = new_mode;
285 threads_per_subcore = threads_per_core / subcores_per_core;
286
287 /* Make sure the new mode is written before we exit */
288 mb();
289 }
290
291 return 0;
292}
293
294static int set_subcores_per_core(int new_mode)
295{
296 struct split_state *state;
297 int cpu;
298
299 if (kvm_hv_mode_active()) {
300 pr_err("Unable to change split core mode while KVM active.\n");
301 return -EBUSY;
302 }
303
304 /*
305 * We are only called at boot, or from the sysfs write. If that ever
306 * changes we'll need a lock here.
307 */
308 BUG_ON(new_mode < 1 || new_mode > 4 || new_mode == 3);
309
310 for_each_present_cpu(cpu) {
311 state = &per_cpu(split_state, cpu);
312 state->step = SYNC_STEP_INITIAL;
313 state->master = 0;
314 }
315
316 get_online_cpus();
317
318 /* This cpu will update the globals before exiting stop machine */
319 this_cpu_ptr(&split_state)->master = 1;
320
321 /* Ensure state is consistent before we call the other cpus */
322 mb();
323
324 stop_machine(cpu_update_split_mode, &new_mode, cpu_online_mask);
325
326 put_online_cpus();
327
328 return 0;
329}
330
331static ssize_t __used store_subcores_per_core(struct device *dev,
332 struct device_attribute *attr, const char *buf,
333 size_t count)
334{
335 unsigned long val;
336 int rc;
337
338 /* We are serialised by the attribute lock */
339
340 rc = sscanf(buf, "%lx", &val);
341 if (rc != 1)
342 return -EINVAL;
343
344 switch (val) {
345 case 1:
346 case 2:
347 case 4:
348 if (subcores_per_core == val)
349 /* Nothing to do */
350 goto out;
351 break;
352 default:
353 return -EINVAL;
354 }
355
356 rc = set_subcores_per_core(val);
357 if (rc)
358 return rc;
359
360out:
361 return count;
362}
363
364static ssize_t show_subcores_per_core(struct device *dev,
365 struct device_attribute *attr, char *buf)
366{
367 return sprintf(buf, "%x\n", subcores_per_core);
368}
369
370static DEVICE_ATTR(subcores_per_core, 0644,
371 show_subcores_per_core, store_subcores_per_core);
372
373static int subcore_init(void)
374{
375 if (!cpu_has_feature(CPU_FTR_ARCH_207S))
376 return 0;
377
378 /*
379 * We need all threads in a core to be present to split/unsplit so
380 * continue only if max_cpus are aligned to threads_per_core.
381 */
382 if (setup_max_cpus % threads_per_core)
383 return 0;
384
385 BUG_ON(!alloc_cpumask_var(&cpu_offline_mask, GFP_KERNEL));
386
387 set_subcores_per_core(1);
388
389 return device_create_file(cpu_subsys.dev_root,
390 &dev_attr_subcores_per_core);
391}
392machine_device_initcall(powernv, subcore_init);
diff --git a/arch/powerpc/platforms/powernv/subcore.h b/arch/powerpc/platforms/powernv/subcore.h
new file mode 100644
index 000000000000..148abc91debf
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/subcore.h
@@ -0,0 +1,18 @@
1/*
2 * Copyright 2013, Michael Ellerman, IBM Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10/* These are ordered and tested with <= */
11#define SYNC_STEP_INITIAL 0
12#define SYNC_STEP_UNSPLIT 1 /* Set by secondary when it sees unsplit */
13#define SYNC_STEP_REAL_MODE 2 /* Set by secondary when in real mode */
14#define SYNC_STEP_FINISHED 3 /* Set by secondary when split/unsplit is done */
15
16#ifndef __ASSEMBLY__
17void split_core_secondary_loop(u8 *state);
18#endif