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(&regs);
2708c2ecf20Sopenharmony_ci			crash_ipi_callback(&regs);
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