18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SMP support for PowerNV machines. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/sched/hotplug.h> 128c2ecf20Sopenharmony_ci#include <linux/smp.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 178c2ecf20Sopenharmony_ci#include <linux/cpu.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/irq.h> 208c2ecf20Sopenharmony_ci#include <asm/smp.h> 218c2ecf20Sopenharmony_ci#include <asm/paca.h> 228c2ecf20Sopenharmony_ci#include <asm/machdep.h> 238c2ecf20Sopenharmony_ci#include <asm/cputable.h> 248c2ecf20Sopenharmony_ci#include <asm/firmware.h> 258c2ecf20Sopenharmony_ci#include <asm/vdso_datapage.h> 268c2ecf20Sopenharmony_ci#include <asm/cputhreads.h> 278c2ecf20Sopenharmony_ci#include <asm/xics.h> 288c2ecf20Sopenharmony_ci#include <asm/xive.h> 298c2ecf20Sopenharmony_ci#include <asm/opal.h> 308c2ecf20Sopenharmony_ci#include <asm/runlatch.h> 318c2ecf20Sopenharmony_ci#include <asm/code-patching.h> 328c2ecf20Sopenharmony_ci#include <asm/dbell.h> 338c2ecf20Sopenharmony_ci#include <asm/kvm_ppc.h> 348c2ecf20Sopenharmony_ci#include <asm/ppc-opcode.h> 358c2ecf20Sopenharmony_ci#include <asm/cpuidle.h> 368c2ecf20Sopenharmony_ci#include <asm/kexec.h> 378c2ecf20Sopenharmony_ci#include <asm/reg.h> 388c2ecf20Sopenharmony_ci#include <asm/powernv.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include "powernv.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#ifdef DEBUG 438c2ecf20Sopenharmony_ci#include <asm/udbg.h> 448c2ecf20Sopenharmony_ci#define DBG(fmt...) udbg_printf(fmt) 458c2ecf20Sopenharmony_ci#else 468c2ecf20Sopenharmony_ci#define DBG(fmt...) do { } while (0) 478c2ecf20Sopenharmony_ci#endif 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void pnv_smp_setup_cpu(int cpu) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * P9 workaround for CI vector load (see traps.c), 538c2ecf20Sopenharmony_ci * enable the corresponding HMI interrupt 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci if (pvr_version_is(PVR_POWER9)) 568c2ecf20Sopenharmony_ci mtspr(SPRN_HMEER, mfspr(SPRN_HMEER) | PPC_BIT(17)); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (xive_enabled()) 598c2ecf20Sopenharmony_ci xive_smp_setup_cpu(); 608c2ecf20Sopenharmony_ci else if (cpu != boot_cpuid) 618c2ecf20Sopenharmony_ci xics_setup_cpu(); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int pnv_smp_kick_cpu(int nr) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci unsigned int pcpu; 678c2ecf20Sopenharmony_ci unsigned long start_here = 688c2ecf20Sopenharmony_ci __pa(ppc_function_entry(generic_secondary_smp_init)); 698c2ecf20Sopenharmony_ci long rc; 708c2ecf20Sopenharmony_ci uint8_t status; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (nr < 0 || nr >= nr_cpu_ids) 738c2ecf20Sopenharmony_ci return -EINVAL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci pcpu = get_hard_smp_processor_id(nr); 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * If we already started or OPAL is not supported, we just 788c2ecf20Sopenharmony_ci * kick the CPU via the PACA 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci if (paca_ptrs[nr]->cpu_start || !firmware_has_feature(FW_FEATURE_OPAL)) 818c2ecf20Sopenharmony_ci goto kick; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * At this point, the CPU can either be spinning on the way in 858c2ecf20Sopenharmony_ci * from kexec or be inside OPAL waiting to be started for the 868c2ecf20Sopenharmony_ci * first time. OPAL v3 allows us to query OPAL to know if it 878c2ecf20Sopenharmony_ci * has the CPUs, so we do that 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci rc = opal_query_cpu_status(pcpu, &status); 908c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS) { 918c2ecf20Sopenharmony_ci pr_warn("OPAL Error %ld querying CPU %d state\n", rc, nr); 928c2ecf20Sopenharmony_ci return -ENODEV; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * Already started, just kick it, probably coming from 978c2ecf20Sopenharmony_ci * kexec and spinning 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci if (status == OPAL_THREAD_STARTED) 1008c2ecf20Sopenharmony_ci goto kick; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * Available/inactive, let's kick it 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci if (status == OPAL_THREAD_INACTIVE) { 1068c2ecf20Sopenharmony_ci pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); 1078c2ecf20Sopenharmony_ci rc = opal_start_cpu(pcpu, start_here); 1088c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS) { 1098c2ecf20Sopenharmony_ci pr_warn("OPAL Error %ld starting CPU %d\n", rc, nr); 1108c2ecf20Sopenharmony_ci return -ENODEV; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } else { 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * An unavailable CPU (or any other unknown status) 1158c2ecf20Sopenharmony_ci * shouldn't be started. It should also 1168c2ecf20Sopenharmony_ci * not be in the possible map but currently it can 1178c2ecf20Sopenharmony_ci * happen 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci pr_devel("OPAL: CPU %d (HW 0x%x) is unavailable" 1208c2ecf20Sopenharmony_ci " (status %d)...\n", nr, pcpu, status); 1218c2ecf20Sopenharmony_ci return -ENODEV; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cikick: 1258c2ecf20Sopenharmony_ci return smp_generic_kick_cpu(nr); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int pnv_smp_cpu_disable(void) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* This is identical to pSeries... might consolidate by 1358c2ecf20Sopenharmony_ci * moving migrate_irqs_away to a ppc_md with default to 1368c2ecf20Sopenharmony_ci * the generic fixup_irqs. --BenH. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci set_cpu_online(cpu, false); 1398c2ecf20Sopenharmony_ci vdso_data->processorCount--; 1408c2ecf20Sopenharmony_ci if (cpu == boot_cpuid) 1418c2ecf20Sopenharmony_ci boot_cpuid = cpumask_any(cpu_online_mask); 1428c2ecf20Sopenharmony_ci if (xive_enabled()) 1438c2ecf20Sopenharmony_ci xive_smp_disable_cpu(); 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci xics_migrate_irqs_away(); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci cleanup_cpu_mmu_context(); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void pnv_flush_interrupts(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_300)) { 1558c2ecf20Sopenharmony_ci if (xive_enabled()) 1568c2ecf20Sopenharmony_ci xive_flush_interrupt(); 1578c2ecf20Sopenharmony_ci else 1588c2ecf20Sopenharmony_ci icp_opal_flush_interrupt(); 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci icp_native_flush_interrupt(); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void pnv_cpu_offline_self(void) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci unsigned long srr1, unexpected_mask, wmask; 1678c2ecf20Sopenharmony_ci unsigned int cpu; 1688c2ecf20Sopenharmony_ci u64 lpcr_val; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Standard hot unplug procedure */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci idle_task_exit(); 1738c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 1748c2ecf20Sopenharmony_ci DBG("CPU%d offline\n", cpu); 1758c2ecf20Sopenharmony_ci generic_set_cpu_dead(cpu); 1768c2ecf20Sopenharmony_ci smp_wmb(); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci wmask = SRR1_WAKEMASK; 1798c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_207S)) 1808c2ecf20Sopenharmony_ci wmask = SRR1_WAKEMASK_P8; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * This turns the irq soft-disabled state we're called with, into a 1848c2ecf20Sopenharmony_ci * hard-disabled state with pending irq_happened interrupts cleared. 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * PACA_IRQ_DEC - Decrementer should be ignored. 1878c2ecf20Sopenharmony_ci * PACA_IRQ_HMI - Can be ignored, processing is done in real mode. 1888c2ecf20Sopenharmony_ci * PACA_IRQ_DBELL, EE, PMI - Unexpected. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci hard_irq_disable(); 1918c2ecf20Sopenharmony_ci if (generic_check_cpu_restart(cpu)) 1928c2ecf20Sopenharmony_ci goto out; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci unexpected_mask = ~(PACA_IRQ_DEC | PACA_IRQ_HMI | PACA_IRQ_HARD_DIS); 1958c2ecf20Sopenharmony_ci if (local_paca->irq_happened & unexpected_mask) { 1968c2ecf20Sopenharmony_ci if (local_paca->irq_happened & PACA_IRQ_EE) 1978c2ecf20Sopenharmony_ci pnv_flush_interrupts(); 1988c2ecf20Sopenharmony_ci DBG("CPU%d Unexpected exit while offline irq_happened=%lx!\n", 1998c2ecf20Sopenharmony_ci cpu, local_paca->irq_happened); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci local_paca->irq_happened = PACA_IRQ_HARD_DIS; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * We don't want to take decrementer interrupts while we are 2058c2ecf20Sopenharmony_ci * offline, so clear LPCR:PECE1. We keep PECE2 (and 2068c2ecf20Sopenharmony_ci * LPCR_PECE_HVEE on P9) enabled so as to let IPIs in. 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * If the CPU gets woken up by a special wakeup, ensure that 2098c2ecf20Sopenharmony_ci * the SLW engine sets LPCR with decrementer bit cleared, else 2108c2ecf20Sopenharmony_ci * the CPU will come back to the kernel due to a spurious 2118c2ecf20Sopenharmony_ci * wakeup. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci lpcr_val = mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1; 2148c2ecf20Sopenharmony_ci pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci while (!generic_check_cpu_restart(cpu)) { 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * Clear IPI flag, since we don't handle IPIs while 2198c2ecf20Sopenharmony_ci * offline, except for those when changing micro-threading 2208c2ecf20Sopenharmony_ci * mode, which are handled explicitly below, and those 2218c2ecf20Sopenharmony_ci * for coming online, which are handled via 2228c2ecf20Sopenharmony_ci * generic_check_cpu_restart() calls. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci kvmppc_clear_host_ipi(cpu); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci srr1 = pnv_cpu_offline(cpu); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci WARN_ON_ONCE(!irqs_disabled()); 2298c2ecf20Sopenharmony_ci WARN_ON(lazy_irq_pending()); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* 2328c2ecf20Sopenharmony_ci * If the SRR1 value indicates that we woke up due to 2338c2ecf20Sopenharmony_ci * an external interrupt, then clear the interrupt. 2348c2ecf20Sopenharmony_ci * We clear the interrupt before checking for the 2358c2ecf20Sopenharmony_ci * reason, so as to avoid a race where we wake up for 2368c2ecf20Sopenharmony_ci * some other reason, find nothing and clear the interrupt 2378c2ecf20Sopenharmony_ci * just as some other cpu is sending us an interrupt. 2388c2ecf20Sopenharmony_ci * If we returned from power7_nap as a result of 2398c2ecf20Sopenharmony_ci * having finished executing in a KVM guest, then srr1 2408c2ecf20Sopenharmony_ci * contains 0. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci if (((srr1 & wmask) == SRR1_WAKEEE) || 2438c2ecf20Sopenharmony_ci ((srr1 & wmask) == SRR1_WAKEHVI)) { 2448c2ecf20Sopenharmony_ci pnv_flush_interrupts(); 2458c2ecf20Sopenharmony_ci } else if ((srr1 & wmask) == SRR1_WAKEHDBELL) { 2468c2ecf20Sopenharmony_ci unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER); 2478c2ecf20Sopenharmony_ci asm volatile(PPC_MSGCLR(%0) : : "r" (msg)); 2488c2ecf20Sopenharmony_ci } else if ((srr1 & wmask) == SRR1_WAKERESET) { 2498c2ecf20Sopenharmony_ci irq_set_pending_from_srr1(srr1); 2508c2ecf20Sopenharmony_ci /* Does not return */ 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci smp_mb(); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * For kdump kernels, we process the ipi and jump to 2578c2ecf20Sopenharmony_ci * crash_ipi_callback 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci if (kdump_in_progress()) { 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * If we got to this point, we've not used 2628c2ecf20Sopenharmony_ci * NMI's, otherwise we would have gone 2638c2ecf20Sopenharmony_ci * via the SRR1_WAKERESET path. We are 2648c2ecf20Sopenharmony_ci * using regular IPI's for waking up offline 2658c2ecf20Sopenharmony_ci * threads. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci struct pt_regs regs; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ppc_save_regs(®s); 2708c2ecf20Sopenharmony_ci crash_ipi_callback(®s); 2718c2ecf20Sopenharmony_ci /* Does not return */ 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (cpu_core_split_required()) 2758c2ecf20Sopenharmony_ci continue; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (srr1 && !generic_check_cpu_restart(cpu)) 2788c2ecf20Sopenharmony_ci DBG("CPU%d Unexpected exit while offline srr1=%lx!\n", 2798c2ecf20Sopenharmony_ci cpu, srr1); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * Re-enable decrementer interrupts in LPCR. 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * Further, we want stop states to be woken up by decrementer 2878c2ecf20Sopenharmony_ci * for non-hotplug cases. So program the LPCR via stop api as 2888c2ecf20Sopenharmony_ci * well. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci lpcr_val = mfspr(SPRN_LPCR) | (u64)LPCR_PECE1; 2918c2ecf20Sopenharmony_ci pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val); 2928c2ecf20Sopenharmony_ciout: 2938c2ecf20Sopenharmony_ci DBG("CPU%d coming online...\n", cpu); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int pnv_cpu_bootable(unsigned int nr) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Starting with POWER8, the subcore logic relies on all threads of a 3028c2ecf20Sopenharmony_ci * core being booted so that they can participate in split mode 3038c2ecf20Sopenharmony_ci * switches. So on those machines we ignore the smt_enabled_at_boot 3048c2ecf20Sopenharmony_ci * setting (smt-enabled on the kernel command line). 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_207S)) 3078c2ecf20Sopenharmony_ci return 1; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return smp_generic_cpu_bootable(nr); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int pnv_smp_prepare_cpu(int cpu) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci if (xive_enabled()) 3158c2ecf20Sopenharmony_ci return xive_smp_prepare_cpu(cpu); 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* Cause IPI as setup by the interrupt controller (xics or xive) */ 3208c2ecf20Sopenharmony_cistatic void (*ic_cause_ipi)(int cpu); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void pnv_cause_ipi(int cpu) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci if (doorbell_try_core_ipi(cpu)) 3258c2ecf20Sopenharmony_ci return; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ic_cause_ipi(cpu); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void __init pnv_smp_probe(void) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci if (xive_enabled()) 3338c2ecf20Sopenharmony_ci xive_smp_probe(); 3348c2ecf20Sopenharmony_ci else 3358c2ecf20Sopenharmony_ci xics_smp_probe(); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_DBELL)) { 3388c2ecf20Sopenharmony_ci ic_cause_ipi = smp_ops->cause_ipi; 3398c2ecf20Sopenharmony_ci WARN_ON(!ic_cause_ipi); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_300)) 3428c2ecf20Sopenharmony_ci smp_ops->cause_ipi = doorbell_global_ipi; 3438c2ecf20Sopenharmony_ci else 3448c2ecf20Sopenharmony_ci smp_ops->cause_ipi = pnv_cause_ipi; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int pnv_system_reset_exception(struct pt_regs *regs) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci if (smp_handle_nmi_ipi(regs)) 3518c2ecf20Sopenharmony_ci return 1; 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int pnv_cause_nmi_ipi(int cpu) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int64_t rc; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (cpu >= 0) { 3608c2ecf20Sopenharmony_ci int h = get_hard_smp_processor_id(cpu); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (opal_check_token(OPAL_QUIESCE)) 3638c2ecf20Sopenharmony_ci opal_quiesce(QUIESCE_HOLD, h); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci rc = opal_signal_system_reset(h); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (opal_check_token(OPAL_QUIESCE)) 3688c2ecf20Sopenharmony_ci opal_quiesce(QUIESCE_RESUME, h); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS) 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci return 1; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci } else if (cpu == NMI_IPI_ALL_OTHERS) { 3758c2ecf20Sopenharmony_ci bool success = true; 3768c2ecf20Sopenharmony_ci int c; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (opal_check_token(OPAL_QUIESCE)) 3798c2ecf20Sopenharmony_ci opal_quiesce(QUIESCE_HOLD, -1); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * We do not use broadcasts (yet), because it's not clear 3838c2ecf20Sopenharmony_ci * exactly what semantics Linux wants or the firmware should 3848c2ecf20Sopenharmony_ci * provide. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci for_each_online_cpu(c) { 3878c2ecf20Sopenharmony_ci if (c == smp_processor_id()) 3888c2ecf20Sopenharmony_ci continue; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci rc = opal_signal_system_reset( 3918c2ecf20Sopenharmony_ci get_hard_smp_processor_id(c)); 3928c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS) 3938c2ecf20Sopenharmony_ci success = false; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (opal_check_token(OPAL_QUIESCE)) 3978c2ecf20Sopenharmony_ci opal_quiesce(QUIESCE_RESUME, -1); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (success) 4008c2ecf20Sopenharmony_ci return 1; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * Caller will fall back to doorbells, which may pick 4048c2ecf20Sopenharmony_ci * up the remainders. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic struct smp_ops_t pnv_smp_ops = { 4128c2ecf20Sopenharmony_ci .message_pass = NULL, /* Use smp_muxed_ipi_message_pass */ 4138c2ecf20Sopenharmony_ci .cause_ipi = NULL, /* Filled at runtime by pnv_smp_probe() */ 4148c2ecf20Sopenharmony_ci .cause_nmi_ipi = NULL, 4158c2ecf20Sopenharmony_ci .probe = pnv_smp_probe, 4168c2ecf20Sopenharmony_ci .prepare_cpu = pnv_smp_prepare_cpu, 4178c2ecf20Sopenharmony_ci .kick_cpu = pnv_smp_kick_cpu, 4188c2ecf20Sopenharmony_ci .setup_cpu = pnv_smp_setup_cpu, 4198c2ecf20Sopenharmony_ci .cpu_bootable = pnv_cpu_bootable, 4208c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 4218c2ecf20Sopenharmony_ci .cpu_disable = pnv_smp_cpu_disable, 4228c2ecf20Sopenharmony_ci .cpu_die = generic_cpu_die, 4238c2ecf20Sopenharmony_ci .cpu_offline_self = pnv_cpu_offline_self, 4248c2ecf20Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/* This is called very early during platform setup_arch */ 4288c2ecf20Sopenharmony_civoid __init pnv_smp_init(void) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci if (opal_check_token(OPAL_SIGNAL_SYSTEM_RESET)) { 4318c2ecf20Sopenharmony_ci ppc_md.system_reset_exception = pnv_system_reset_exception; 4328c2ecf20Sopenharmony_ci pnv_smp_ops.cause_nmi_ipi = pnv_cause_nmi_ipi; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci smp_ops = &pnv_smp_ops; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 4378c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 4388c2ecf20Sopenharmony_ci crash_wake_offline = 1; 4398c2ecf20Sopenharmony_ci#endif 4408c2ecf20Sopenharmony_ci#endif 4418c2ecf20Sopenharmony_ci} 442