Thomas Gleixner | 2874c5f | 2019-05-27 08:55:01 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 2 | /* |
| 3 | * SMP support for PowerNV machines. |
| 4 | * |
| 5 | * Copyright 2011 IBM Corp. |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 6 | */ |
| 7 | |
| 8 | #include <linux/kernel.h> |
| 9 | #include <linux/module.h> |
| 10 | #include <linux/sched.h> |
Ingo Molnar | ef8bd77 | 2017-02-08 18:51:36 +0100 | [diff] [blame] | 11 | #include <linux/sched/hotplug.h> |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 12 | #include <linux/smp.h> |
| 13 | #include <linux/interrupt.h> |
| 14 | #include <linux/delay.h> |
| 15 | #include <linux/init.h> |
| 16 | #include <linux/spinlock.h> |
| 17 | #include <linux/cpu.h> |
| 18 | |
| 19 | #include <asm/irq.h> |
| 20 | #include <asm/smp.h> |
| 21 | #include <asm/paca.h> |
| 22 | #include <asm/machdep.h> |
| 23 | #include <asm/cputable.h> |
| 24 | #include <asm/firmware.h> |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 25 | #include <asm/vdso_datapage.h> |
| 26 | #include <asm/cputhreads.h> |
| 27 | #include <asm/xics.h> |
Benjamin Herrenschmidt | 243e251 | 2017-04-05 17:54:50 +1000 | [diff] [blame] | 28 | #include <asm/xive.h> |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 29 | #include <asm/opal.h> |
Preeti U Murthy | f203891 | 2014-04-11 16:01:48 +0530 | [diff] [blame] | 30 | #include <asm/runlatch.h> |
Anton Blanchard | 2751b62 | 2014-03-11 11:54:06 +1100 | [diff] [blame] | 31 | #include <asm/code-patching.h> |
Michael Neuling | d4e58e5 | 2014-06-11 15:59:28 +1000 | [diff] [blame] | 32 | #include <asm/dbell.h> |
Paul Mackerras | 755563b | 2015-03-19 19:29:01 +1100 | [diff] [blame] | 33 | #include <asm/kvm_ppc.h> |
| 34 | #include <asm/ppc-opcode.h> |
Gautham R. Shenoy | a7cd88d | 2017-03-22 20:34:14 +0530 | [diff] [blame] | 35 | #include <asm/cpuidle.h> |
Balbir Singh | 4145f35 | 2017-12-15 19:14:55 +1100 | [diff] [blame] | 36 | #include <asm/kexec.h> |
| 37 | #include <asm/reg.h> |
Paul Mackerras | 19f8a5b | 2019-02-12 11:58:29 +1100 | [diff] [blame] | 38 | #include <asm/powernv.h> |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 39 | |
| 40 | #include "powernv.h" |
| 41 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 42 | #ifdef DEBUG |
| 43 | #include <asm/udbg.h> |
| 44 | #define DBG(fmt...) udbg_printf(fmt) |
| 45 | #else |
| 46 | #define DBG(fmt...) |
| 47 | #endif |
| 48 | |
Paul Gortmaker | 061d19f | 2013-06-24 15:30:09 -0400 | [diff] [blame] | 49 | static void pnv_smp_setup_cpu(int cpu) |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 50 | { |
Michael Neuling | 5080332 | 2017-09-15 15:25:48 +1000 | [diff] [blame] | 51 | /* |
| 52 | * P9 workaround for CI vector load (see traps.c), |
| 53 | * enable the corresponding HMI interrupt |
| 54 | */ |
| 55 | if (pvr_version_is(PVR_POWER9)) |
| 56 | mtspr(SPRN_HMEER, mfspr(SPRN_HMEER) | PPC_BIT(17)); |
| 57 | |
Benjamin Herrenschmidt | 243e251 | 2017-04-05 17:54:50 +1000 | [diff] [blame] | 58 | if (xive_enabled()) |
| 59 | xive_smp_setup_cpu(); |
| 60 | else if (cpu != boot_cpuid) |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 61 | xics_setup_cpu(); |
| 62 | } |
| 63 | |
Anton Blanchard | e51df2c | 2014-08-20 08:55:18 +1000 | [diff] [blame] | 64 | static int pnv_smp_kick_cpu(int nr) |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 65 | { |
Santosh Sivaraj | 76d98ab | 2017-07-04 09:52:46 +0530 | [diff] [blame] | 66 | unsigned int pcpu; |
Anton Blanchard | 2751b62 | 2014-03-11 11:54:06 +1100 | [diff] [blame] | 67 | unsigned long start_here = |
| 68 | __pa(ppc_function_entry(generic_secondary_smp_init)); |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 69 | long rc; |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 70 | uint8_t status; |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 71 | |
Santosh Sivaraj | c642af9 | 2017-06-27 12:30:06 +0530 | [diff] [blame] | 72 | if (nr < 0 || nr >= nr_cpu_ids) |
Santosh Sivaraj | f8d0d5d | 2017-06-27 12:30:05 +0530 | [diff] [blame] | 73 | return -EINVAL; |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 74 | |
Santosh Sivaraj | 76d98ab | 2017-07-04 09:52:46 +0530 | [diff] [blame] | 75 | pcpu = get_hard_smp_processor_id(nr); |
Benjamin Herrenschmidt | b2b4858 | 2013-05-14 15:12:31 +1000 | [diff] [blame] | 76 | /* |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 77 | * If we already started or OPAL is not supported, we just |
Benjamin Herrenschmidt | b2b4858 | 2013-05-14 15:12:31 +1000 | [diff] [blame] | 78 | * kick the CPU via the PACA |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 79 | */ |
Nicholas Piggin | d2e6007 | 2018-02-14 01:08:12 +1000 | [diff] [blame] | 80 | if (paca_ptrs[nr]->cpu_start || !firmware_has_feature(FW_FEATURE_OPAL)) |
Benjamin Herrenschmidt | b2b4858 | 2013-05-14 15:12:31 +1000 | [diff] [blame] | 81 | goto kick; |
| 82 | |
| 83 | /* |
| 84 | * At this point, the CPU can either be spinning on the way in |
| 85 | * from kexec or be inside OPAL waiting to be started for the |
| 86 | * first time. OPAL v3 allows us to query OPAL to know if it |
| 87 | * has the CPUs, so we do that |
| 88 | */ |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 89 | rc = opal_query_cpu_status(pcpu, &status); |
| 90 | if (rc != OPAL_SUCCESS) { |
| 91 | pr_warn("OPAL Error %ld querying CPU %d state\n", rc, nr); |
| 92 | return -ENODEV; |
| 93 | } |
Benjamin Herrenschmidt | b2b4858 | 2013-05-14 15:12:31 +1000 | [diff] [blame] | 94 | |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 95 | /* |
| 96 | * Already started, just kick it, probably coming from |
| 97 | * kexec and spinning |
| 98 | */ |
| 99 | if (status == OPAL_THREAD_STARTED) |
| 100 | goto kick; |
| 101 | |
| 102 | /* |
| 103 | * Available/inactive, let's kick it |
| 104 | */ |
| 105 | if (status == OPAL_THREAD_INACTIVE) { |
| 106 | pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); |
| 107 | rc = opal_start_cpu(pcpu, start_here); |
Benjamin Herrenschmidt | 4ea9008 | 2013-05-03 17:21:00 +0000 | [diff] [blame] | 108 | if (rc != OPAL_SUCCESS) { |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 109 | pr_warn("OPAL Error %ld starting CPU %d\n", rc, nr); |
Benjamin Herrenschmidt | b2b4858 | 2013-05-14 15:12:31 +1000 | [diff] [blame] | 110 | return -ENODEV; |
| 111 | } |
| 112 | } else { |
| 113 | /* |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 114 | * An unavailable CPU (or any other unknown status) |
| 115 | * shouldn't be started. It should also |
| 116 | * not be in the possible map but currently it can |
| 117 | * happen |
Benjamin Herrenschmidt | b2b4858 | 2013-05-14 15:12:31 +1000 | [diff] [blame] | 118 | */ |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 119 | pr_devel("OPAL: CPU %d (HW 0x%x) is unavailable" |
| 120 | " (status %d)...\n", nr, pcpu, status); |
| 121 | return -ENODEV; |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 122 | } |
Stewart Smith | e4d54f7 | 2015-12-09 17:18:20 +1100 | [diff] [blame] | 123 | |
| 124 | kick: |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 125 | return smp_generic_kick_cpu(nr); |
| 126 | } |
| 127 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 128 | #ifdef CONFIG_HOTPLUG_CPU |
| 129 | |
| 130 | static int pnv_smp_cpu_disable(void) |
| 131 | { |
| 132 | int cpu = smp_processor_id(); |
| 133 | |
| 134 | /* This is identical to pSeries... might consolidate by |
| 135 | * moving migrate_irqs_away to a ppc_md with default to |
| 136 | * the generic fixup_irqs. --BenH. |
| 137 | */ |
| 138 | set_cpu_online(cpu, false); |
| 139 | vdso_data->processorCount--; |
| 140 | if (cpu == boot_cpuid) |
| 141 | boot_cpuid = cpumask_any(cpu_online_mask); |
Benjamin Herrenschmidt | 243e251 | 2017-04-05 17:54:50 +1000 | [diff] [blame] | 142 | if (xive_enabled()) |
| 143 | xive_smp_disable_cpu(); |
| 144 | else |
| 145 | xics_migrate_irqs_away(); |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 146 | return 0; |
| 147 | } |
| 148 | |
Nicholas Piggin | 7d64750 | 2019-10-22 21:58:14 +1000 | [diff] [blame^] | 149 | static void pnv_flush_interrupts(void) |
| 150 | { |
| 151 | if (cpu_has_feature(CPU_FTR_ARCH_300)) { |
| 152 | if (xive_enabled()) |
| 153 | xive_flush_interrupt(); |
| 154 | else |
| 155 | icp_opal_flush_interrupt(); |
| 156 | } else { |
| 157 | icp_native_flush_interrupt(); |
| 158 | } |
| 159 | } |
| 160 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 161 | static void pnv_smp_cpu_kill_self(void) |
| 162 | { |
Nicholas Piggin | 7d64750 | 2019-10-22 21:58:14 +1000 | [diff] [blame^] | 163 | unsigned long srr1, unexpected_mask, wmask; |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 164 | unsigned int cpu; |
Paul Mackerras | 19f8a5b | 2019-02-12 11:58:29 +1100 | [diff] [blame] | 165 | u64 lpcr_val; |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 166 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 167 | /* Standard hot unplug procedure */ |
Nicholas Piggin | 2525db0 | 2017-06-13 23:05:46 +1000 | [diff] [blame] | 168 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 169 | idle_task_exit(); |
| 170 | current->active_mm = NULL; /* for sanity */ |
| 171 | cpu = smp_processor_id(); |
| 172 | DBG("CPU%d offline\n", cpu); |
| 173 | generic_set_cpu_dead(cpu); |
| 174 | smp_wmb(); |
| 175 | |
Paul Mackerras | 755563b | 2015-03-19 19:29:01 +1100 | [diff] [blame] | 176 | wmask = SRR1_WAKEMASK; |
| 177 | if (cpu_has_feature(CPU_FTR_ARCH_207S)) |
| 178 | wmask = SRR1_WAKEMASK_P8; |
| 179 | |
Paul Mackerras | 19f8a5b | 2019-02-12 11:58:29 +1100 | [diff] [blame] | 180 | /* |
Nicholas Piggin | 7d64750 | 2019-10-22 21:58:14 +1000 | [diff] [blame^] | 181 | * This turns the irq soft-disabled state we're called with, into a |
| 182 | * hard-disabled state with pending irq_happened interrupts cleared. |
| 183 | * |
| 184 | * PACA_IRQ_DEC - Decrementer should be ignored. |
| 185 | * PACA_IRQ_HMI - Can be ignored, processing is done in real mode. |
| 186 | * PACA_IRQ_DBELL, EE, PMI - Unexpected. |
| 187 | */ |
| 188 | hard_irq_disable(); |
| 189 | if (generic_check_cpu_restart(cpu)) |
| 190 | goto out; |
| 191 | |
| 192 | unexpected_mask = ~(PACA_IRQ_DEC | PACA_IRQ_HMI | PACA_IRQ_HARD_DIS); |
| 193 | if (local_paca->irq_happened & unexpected_mask) { |
| 194 | if (local_paca->irq_happened & PACA_IRQ_EE) |
| 195 | pnv_flush_interrupts(); |
| 196 | DBG("CPU%d Unexpected exit while offline irq_happened=%lx!\n", |
| 197 | cpu, local_paca->irq_happened); |
| 198 | } |
| 199 | local_paca->irq_happened = PACA_IRQ_HARD_DIS; |
| 200 | |
| 201 | /* |
Paul Mackerras | 19f8a5b | 2019-02-12 11:58:29 +1100 | [diff] [blame] | 202 | * We don't want to take decrementer interrupts while we are |
| 203 | * offline, so clear LPCR:PECE1. We keep PECE2 (and |
| 204 | * LPCR_PECE_HVEE on P9) enabled so as to let IPIs in. |
| 205 | * |
| 206 | * If the CPU gets woken up by a special wakeup, ensure that |
| 207 | * the SLW engine sets LPCR with decrementer bit cleared, else |
| 208 | * the CPU will come back to the kernel due to a spurious |
| 209 | * wakeup. |
| 210 | */ |
| 211 | lpcr_val = mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1; |
| 212 | pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val); |
| 213 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 214 | while (!generic_check_cpu_restart(cpu)) { |
Paul Mackerras | 53c656c | 2015-10-21 16:06:24 +1100 | [diff] [blame] | 215 | /* |
| 216 | * Clear IPI flag, since we don't handle IPIs while |
| 217 | * offline, except for those when changing micro-threading |
| 218 | * mode, which are handled explicitly below, and those |
| 219 | * for coming online, which are handled via |
| 220 | * generic_check_cpu_restart() calls. |
| 221 | */ |
Michael Roth | 3a83f67 | 2019-09-11 17:31:55 -0500 | [diff] [blame] | 222 | kvmppc_clear_host_ipi(cpu); |
Shreyas B. Prabhu | 77b54e9 | 2014-12-10 00:26:53 +0530 | [diff] [blame] | 223 | |
Gautham R. Shenoy | a7cd88d | 2017-03-22 20:34:14 +0530 | [diff] [blame] | 224 | srr1 = pnv_cpu_offline(cpu); |
Michael Ellerman | e218602 | 2014-05-23 18:15:30 +1000 | [diff] [blame] | 225 | |
Nicholas Piggin | 7d64750 | 2019-10-22 21:58:14 +1000 | [diff] [blame^] | 226 | WARN_ON_ONCE(!irqs_disabled()); |
Nicholas Piggin | 2525db0 | 2017-06-13 23:05:46 +1000 | [diff] [blame] | 227 | WARN_ON(lazy_irq_pending()); |
| 228 | |
Paul Mackerras | 56548fc | 2014-12-03 14:48:40 +1100 | [diff] [blame] | 229 | /* |
| 230 | * If the SRR1 value indicates that we woke up due to |
| 231 | * an external interrupt, then clear the interrupt. |
| 232 | * We clear the interrupt before checking for the |
| 233 | * reason, so as to avoid a race where we wake up for |
| 234 | * some other reason, find nothing and clear the interrupt |
| 235 | * just as some other cpu is sending us an interrupt. |
| 236 | * If we returned from power7_nap as a result of |
| 237 | * having finished executing in a KVM guest, then srr1 |
| 238 | * contains 0. |
| 239 | */ |
Paul Mackerras | 53c656c | 2015-10-21 16:06:24 +1100 | [diff] [blame] | 240 | if (((srr1 & wmask) == SRR1_WAKEEE) || |
Nicholas Piggin | 2525db0 | 2017-06-13 23:05:46 +1000 | [diff] [blame] | 241 | ((srr1 & wmask) == SRR1_WAKEHVI)) { |
Nicholas Piggin | 7d64750 | 2019-10-22 21:58:14 +1000 | [diff] [blame^] | 242 | pnv_flush_interrupts(); |
Paul Mackerras | 755563b | 2015-03-19 19:29:01 +1100 | [diff] [blame] | 243 | } else if ((srr1 & wmask) == SRR1_WAKEHDBELL) { |
| 244 | unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER); |
| 245 | asm volatile(PPC_MSGCLR(%0) : : "r" (msg)); |
Balbir Singh | 4145f35 | 2017-12-15 19:14:55 +1100 | [diff] [blame] | 246 | } else if ((srr1 & wmask) == SRR1_WAKERESET) { |
| 247 | irq_set_pending_from_srr1(srr1); |
| 248 | /* Does not return */ |
Paul Mackerras | 56548fc | 2014-12-03 14:48:40 +1100 | [diff] [blame] | 249 | } |
Balbir Singh | 4145f35 | 2017-12-15 19:14:55 +1100 | [diff] [blame] | 250 | |
Paul Mackerras | 53c656c | 2015-10-21 16:06:24 +1100 | [diff] [blame] | 251 | smp_mb(); |
Michael Ellerman | e218602 | 2014-05-23 18:15:30 +1000 | [diff] [blame] | 252 | |
Balbir Singh | 4145f35 | 2017-12-15 19:14:55 +1100 | [diff] [blame] | 253 | /* |
| 254 | * For kdump kernels, we process the ipi and jump to |
| 255 | * crash_ipi_callback |
| 256 | */ |
| 257 | if (kdump_in_progress()) { |
| 258 | /* |
| 259 | * If we got to this point, we've not used |
| 260 | * NMI's, otherwise we would have gone |
| 261 | * via the SRR1_WAKERESET path. We are |
| 262 | * using regular IPI's for waking up offline |
| 263 | * threads. |
| 264 | */ |
| 265 | struct pt_regs regs; |
| 266 | |
| 267 | ppc_save_regs(®s); |
| 268 | crash_ipi_callback(®s); |
| 269 | /* Does not return */ |
| 270 | } |
| 271 | |
Michael Ellerman | e218602 | 2014-05-23 18:15:30 +1000 | [diff] [blame] | 272 | if (cpu_core_split_required()) |
| 273 | continue; |
| 274 | |
Paul Mackerras | 53c656c | 2015-10-21 16:06:24 +1100 | [diff] [blame] | 275 | if (srr1 && !generic_check_cpu_restart(cpu)) |
Nicholas Piggin | 2525db0 | 2017-06-13 23:05:46 +1000 | [diff] [blame] | 276 | DBG("CPU%d Unexpected exit while offline srr1=%lx!\n", |
| 277 | cpu, srr1); |
| 278 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 279 | } |
Benjamin Herrenschmidt | 9b25671 | 2017-02-07 11:35:31 +1100 | [diff] [blame] | 280 | |
Paul Mackerras | 19f8a5b | 2019-02-12 11:58:29 +1100 | [diff] [blame] | 281 | /* |
| 282 | * Re-enable decrementer interrupts in LPCR. |
| 283 | * |
| 284 | * Further, we want stop states to be woken up by decrementer |
| 285 | * for non-hotplug cases. So program the LPCR via stop api as |
| 286 | * well. |
| 287 | */ |
| 288 | lpcr_val = mfspr(SPRN_LPCR) | (u64)LPCR_PECE1; |
| 289 | pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val); |
Nicholas Piggin | 7d64750 | 2019-10-22 21:58:14 +1000 | [diff] [blame^] | 290 | out: |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 291 | DBG("CPU%d coming online...\n", cpu); |
| 292 | } |
| 293 | |
| 294 | #endif /* CONFIG_HOTPLUG_CPU */ |
| 295 | |
Greg Kurz | d70a54e | 2014-12-12 12:37:40 +0100 | [diff] [blame] | 296 | static int pnv_cpu_bootable(unsigned int nr) |
| 297 | { |
| 298 | /* |
| 299 | * Starting with POWER8, the subcore logic relies on all threads of a |
| 300 | * core being booted so that they can participate in split mode |
| 301 | * switches. So on those machines we ignore the smt_enabled_at_boot |
| 302 | * setting (smt-enabled on the kernel command line). |
| 303 | */ |
| 304 | if (cpu_has_feature(CPU_FTR_ARCH_207S)) |
| 305 | return 1; |
| 306 | |
| 307 | return smp_generic_cpu_bootable(nr); |
| 308 | } |
| 309 | |
Benjamin Herrenschmidt | 243e251 | 2017-04-05 17:54:50 +1000 | [diff] [blame] | 310 | static int pnv_smp_prepare_cpu(int cpu) |
| 311 | { |
| 312 | if (xive_enabled()) |
| 313 | return xive_smp_prepare_cpu(cpu); |
| 314 | return 0; |
| 315 | } |
| 316 | |
Michael Ellerman | 45b21cf | 2017-04-26 17:32:38 +1000 | [diff] [blame] | 317 | /* Cause IPI as setup by the interrupt controller (xics or xive) */ |
| 318 | static void (*ic_cause_ipi)(int cpu); |
| 319 | |
Nicholas Piggin | b866cc2 | 2017-04-13 20:16:21 +1000 | [diff] [blame] | 320 | static void pnv_cause_ipi(int cpu) |
| 321 | { |
| 322 | if (doorbell_try_core_ipi(cpu)) |
| 323 | return; |
| 324 | |
Michael Ellerman | 45b21cf | 2017-04-26 17:32:38 +1000 | [diff] [blame] | 325 | ic_cause_ipi(cpu); |
Nicholas Piggin | b866cc2 | 2017-04-13 20:16:21 +1000 | [diff] [blame] | 326 | } |
| 327 | |
Benjamin Herrenschmidt | 243e251 | 2017-04-05 17:54:50 +1000 | [diff] [blame] | 328 | static void __init pnv_smp_probe(void) |
| 329 | { |
| 330 | if (xive_enabled()) |
| 331 | xive_smp_probe(); |
| 332 | else |
| 333 | xics_smp_probe(); |
Nicholas Piggin | b866cc2 | 2017-04-13 20:16:21 +1000 | [diff] [blame] | 334 | |
Nicholas Piggin | 6b3edef | 2017-04-13 20:16:24 +1000 | [diff] [blame] | 335 | if (cpu_has_feature(CPU_FTR_DBELL)) { |
Michael Ellerman | 45b21cf | 2017-04-26 17:32:38 +1000 | [diff] [blame] | 336 | ic_cause_ipi = smp_ops->cause_ipi; |
| 337 | WARN_ON(!ic_cause_ipi); |
| 338 | |
Nicholas Piggin | 2bf1071 | 2018-07-05 18:47:00 +1000 | [diff] [blame] | 339 | if (cpu_has_feature(CPU_FTR_ARCH_300)) |
| 340 | smp_ops->cause_ipi = doorbell_global_ipi; |
| 341 | else |
Nicholas Piggin | 6b3edef | 2017-04-13 20:16:24 +1000 | [diff] [blame] | 342 | smp_ops->cause_ipi = pnv_cause_ipi; |
Nicholas Piggin | b866cc2 | 2017-04-13 20:16:21 +1000 | [diff] [blame] | 343 | } |
Benjamin Herrenschmidt | 243e251 | 2017-04-05 17:54:50 +1000 | [diff] [blame] | 344 | } |
| 345 | |
Nicholas Piggin | e36d0a2 | 2017-09-29 13:29:42 +1000 | [diff] [blame] | 346 | static int pnv_system_reset_exception(struct pt_regs *regs) |
| 347 | { |
| 348 | if (smp_handle_nmi_ipi(regs)) |
| 349 | return 1; |
| 350 | return 0; |
| 351 | } |
| 352 | |
| 353 | static int pnv_cause_nmi_ipi(int cpu) |
| 354 | { |
| 355 | int64_t rc; |
| 356 | |
| 357 | if (cpu >= 0) { |
Nicholas Piggin | ee03b9b | 2018-05-10 22:21:48 +1000 | [diff] [blame] | 358 | int h = get_hard_smp_processor_id(cpu); |
| 359 | |
| 360 | if (opal_check_token(OPAL_QUIESCE)) |
| 361 | opal_quiesce(QUIESCE_HOLD, h); |
| 362 | |
| 363 | rc = opal_signal_system_reset(h); |
| 364 | |
| 365 | if (opal_check_token(OPAL_QUIESCE)) |
| 366 | opal_quiesce(QUIESCE_RESUME, h); |
| 367 | |
Nicholas Piggin | e36d0a2 | 2017-09-29 13:29:42 +1000 | [diff] [blame] | 368 | if (rc != OPAL_SUCCESS) |
| 369 | return 0; |
| 370 | return 1; |
| 371 | |
| 372 | } else if (cpu == NMI_IPI_ALL_OTHERS) { |
| 373 | bool success = true; |
| 374 | int c; |
| 375 | |
Nicholas Piggin | ee03b9b | 2018-05-10 22:21:48 +1000 | [diff] [blame] | 376 | if (opal_check_token(OPAL_QUIESCE)) |
| 377 | opal_quiesce(QUIESCE_HOLD, -1); |
Nicholas Piggin | e36d0a2 | 2017-09-29 13:29:42 +1000 | [diff] [blame] | 378 | |
| 379 | /* |
| 380 | * We do not use broadcasts (yet), because it's not clear |
| 381 | * exactly what semantics Linux wants or the firmware should |
| 382 | * provide. |
| 383 | */ |
| 384 | for_each_online_cpu(c) { |
| 385 | if (c == smp_processor_id()) |
| 386 | continue; |
| 387 | |
| 388 | rc = opal_signal_system_reset( |
| 389 | get_hard_smp_processor_id(c)); |
| 390 | if (rc != OPAL_SUCCESS) |
| 391 | success = false; |
| 392 | } |
Nicholas Piggin | ee03b9b | 2018-05-10 22:21:48 +1000 | [diff] [blame] | 393 | |
| 394 | if (opal_check_token(OPAL_QUIESCE)) |
| 395 | opal_quiesce(QUIESCE_RESUME, -1); |
| 396 | |
Nicholas Piggin | e36d0a2 | 2017-09-29 13:29:42 +1000 | [diff] [blame] | 397 | if (success) |
| 398 | return 1; |
| 399 | |
| 400 | /* |
| 401 | * Caller will fall back to doorbells, which may pick |
| 402 | * up the remainders. |
| 403 | */ |
| 404 | } |
| 405 | |
| 406 | return 0; |
| 407 | } |
| 408 | |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 409 | static struct smp_ops_t pnv_smp_ops = { |
Nicholas Piggin | b866cc2 | 2017-04-13 20:16:21 +1000 | [diff] [blame] | 410 | .message_pass = NULL, /* Use smp_muxed_ipi_message_pass */ |
| 411 | .cause_ipi = NULL, /* Filled at runtime by pnv_smp_probe() */ |
Nicholas Piggin | c64af64 | 2016-12-20 04:30:09 +1000 | [diff] [blame] | 412 | .cause_nmi_ipi = NULL, |
Benjamin Herrenschmidt | 243e251 | 2017-04-05 17:54:50 +1000 | [diff] [blame] | 413 | .probe = pnv_smp_probe, |
| 414 | .prepare_cpu = pnv_smp_prepare_cpu, |
Benjamin Herrenschmidt | 14a43e6 | 2011-09-19 17:44:57 +0000 | [diff] [blame] | 415 | .kick_cpu = pnv_smp_kick_cpu, |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 416 | .setup_cpu = pnv_smp_setup_cpu, |
Greg Kurz | d70a54e | 2014-12-12 12:37:40 +0100 | [diff] [blame] | 417 | .cpu_bootable = pnv_cpu_bootable, |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 418 | #ifdef CONFIG_HOTPLUG_CPU |
| 419 | .cpu_disable = pnv_smp_cpu_disable, |
| 420 | .cpu_die = generic_cpu_die, |
| 421 | #endif /* CONFIG_HOTPLUG_CPU */ |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 422 | }; |
| 423 | |
| 424 | /* This is called very early during platform setup_arch */ |
| 425 | void __init pnv_smp_init(void) |
| 426 | { |
Nicholas Piggin | e36d0a2 | 2017-09-29 13:29:42 +1000 | [diff] [blame] | 427 | if (opal_check_token(OPAL_SIGNAL_SYSTEM_RESET)) { |
| 428 | ppc_md.system_reset_exception = pnv_system_reset_exception; |
| 429 | pnv_smp_ops.cause_nmi_ipi = pnv_cause_nmi_ipi; |
| 430 | } |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 431 | smp_ops = &pnv_smp_ops; |
| 432 | |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 433 | #ifdef CONFIG_HOTPLUG_CPU |
| 434 | ppc_md.cpu_die = pnv_smp_cpu_kill_self; |
Balbir Singh | 4145f35 | 2017-12-15 19:14:55 +1100 | [diff] [blame] | 435 | #ifdef CONFIG_KEXEC_CORE |
| 436 | crash_wake_offline = 1; |
| 437 | #endif |
Benjamin Herrenschmidt | 344eb01 | 2011-09-19 17:44:54 +0000 | [diff] [blame] | 438 | #endif |
Benjamin Herrenschmidt | 55190f8 | 2011-09-19 17:44:52 +0000 | [diff] [blame] | 439 | } |