18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Author: Andy Fleming <afleming@freescale.com>
48c2ecf20Sopenharmony_ci * 	   Kumar Gala <galak@kernel.crashing.org>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2006-2008, 2011-2012, 2015 Freescale Semiconductor Inc.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/stddef.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/sched/hotplug.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/kexec.h>
168c2ecf20Sopenharmony_ci#include <linux/highmem.h>
178c2ecf20Sopenharmony_ci#include <linux/cpu.h>
188c2ecf20Sopenharmony_ci#include <linux/fsl/guts.h>
198c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/machdep.h>
228c2ecf20Sopenharmony_ci#include <asm/page.h>
238c2ecf20Sopenharmony_ci#include <asm/mpic.h>
248c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
258c2ecf20Sopenharmony_ci#include <asm/dbell.h>
268c2ecf20Sopenharmony_ci#include <asm/code-patching.h>
278c2ecf20Sopenharmony_ci#include <asm/cputhreads.h>
288c2ecf20Sopenharmony_ci#include <asm/fsl_pm.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <sysdev/fsl_soc.h>
318c2ecf20Sopenharmony_ci#include <sysdev/mpic.h>
328c2ecf20Sopenharmony_ci#include "smp.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct epapr_spin_table {
358c2ecf20Sopenharmony_ci	u32	addr_h;
368c2ecf20Sopenharmony_ci	u32	addr_l;
378c2ecf20Sopenharmony_ci	u32	r3_h;
388c2ecf20Sopenharmony_ci	u32	r3_l;
398c2ecf20Sopenharmony_ci	u32	reserved;
408c2ecf20Sopenharmony_ci	u32	pir;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic u64 timebase;
448c2ecf20Sopenharmony_cistatic int tb_req;
458c2ecf20Sopenharmony_cistatic int tb_valid;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void mpc85xx_give_timebase(void)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	unsigned long flags;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	local_irq_save(flags);
528c2ecf20Sopenharmony_ci	hard_irq_disable();
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	while (!tb_req)
558c2ecf20Sopenharmony_ci		barrier();
568c2ecf20Sopenharmony_ci	tb_req = 0;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	qoriq_pm_ops->freeze_time_base(true);
598c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * e5500/e6500 have a workaround for erratum A-006958 in place
628c2ecf20Sopenharmony_ci	 * that will reread the timebase until TBL is non-zero.
638c2ecf20Sopenharmony_ci	 * That would be a bad thing when the timebase is frozen.
648c2ecf20Sopenharmony_ci	 *
658c2ecf20Sopenharmony_ci	 * Thus, we read it manually, and instead of checking that
668c2ecf20Sopenharmony_ci	 * TBL is non-zero, we ensure that TB does not change.  We don't
678c2ecf20Sopenharmony_ci	 * do that for the main mftb implementation, because it requires
688c2ecf20Sopenharmony_ci	 * a scratch register
698c2ecf20Sopenharmony_ci	 */
708c2ecf20Sopenharmony_ci	{
718c2ecf20Sopenharmony_ci		u64 prev;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		asm volatile("mfspr %0, %1" : "=r" (timebase) :
748c2ecf20Sopenharmony_ci			     "i" (SPRN_TBRL));
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		do {
778c2ecf20Sopenharmony_ci			prev = timebase;
788c2ecf20Sopenharmony_ci			asm volatile("mfspr %0, %1" : "=r" (timebase) :
798c2ecf20Sopenharmony_ci				     "i" (SPRN_TBRL));
808c2ecf20Sopenharmony_ci		} while (prev != timebase);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci#else
838c2ecf20Sopenharmony_ci	timebase = get_tb();
848c2ecf20Sopenharmony_ci#endif
858c2ecf20Sopenharmony_ci	mb();
868c2ecf20Sopenharmony_ci	tb_valid = 1;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	while (tb_valid)
898c2ecf20Sopenharmony_ci		barrier();
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	qoriq_pm_ops->freeze_time_base(false);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	local_irq_restore(flags);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void mpc85xx_take_timebase(void)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	unsigned long flags;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	local_irq_save(flags);
1018c2ecf20Sopenharmony_ci	hard_irq_disable();
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	tb_req = 1;
1048c2ecf20Sopenharmony_ci	while (!tb_valid)
1058c2ecf20Sopenharmony_ci		barrier();
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	set_tb(timebase >> 32, timebase & 0xffffffff);
1088c2ecf20Sopenharmony_ci	isync();
1098c2ecf20Sopenharmony_ci	tb_valid = 0;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1158c2ecf20Sopenharmony_cistatic void smp_85xx_cpu_offline_self(void)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	unsigned int cpu = smp_processor_id();
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	local_irq_disable();
1208c2ecf20Sopenharmony_ci	hard_irq_disable();
1218c2ecf20Sopenharmony_ci	/* mask all irqs to prevent cpu wakeup */
1228c2ecf20Sopenharmony_ci	qoriq_pm_ops->irq_mask(cpu);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	idle_task_exit();
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	mtspr(SPRN_TCR, 0);
1278c2ecf20Sopenharmony_ci	mtspr(SPRN_TSR, mfspr(SPRN_TSR));
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	generic_set_cpu_dead(cpu);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	cur_cpu_spec->cpu_down_flush();
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	qoriq_pm_ops->cpu_die(cpu);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	while (1)
1368c2ecf20Sopenharmony_ci		;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic void qoriq_cpu_kill(unsigned int cpu)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int i;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	for (i = 0; i < 500; i++) {
1448c2ecf20Sopenharmony_ci		if (is_cpu_dead(cpu)) {
1458c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
1468c2ecf20Sopenharmony_ci			paca_ptrs[cpu]->cpu_start = 0;
1478c2ecf20Sopenharmony_ci#endif
1488c2ecf20Sopenharmony_ci			return;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci		msleep(20);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	pr_err("CPU%d didn't die...\n", cpu);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci#endif
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/*
1578c2ecf20Sopenharmony_ci * To keep it compatible with old boot program which uses
1588c2ecf20Sopenharmony_ci * cache-inhibit spin table, we need to flush the cache
1598c2ecf20Sopenharmony_ci * before accessing spin table to invalidate any staled data.
1608c2ecf20Sopenharmony_ci * We also need to flush the cache after writing to spin
1618c2ecf20Sopenharmony_ci * table to push data out.
1628c2ecf20Sopenharmony_ci */
1638c2ecf20Sopenharmony_cistatic inline void flush_spin_table(void *spin_table)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	flush_dcache_range((ulong)spin_table,
1668c2ecf20Sopenharmony_ci		(ulong)spin_table + sizeof(struct epapr_spin_table));
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic inline u32 read_spin_table_addr_l(void *spin_table)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	flush_dcache_range((ulong)spin_table,
1728c2ecf20Sopenharmony_ci		(ulong)spin_table + sizeof(struct epapr_spin_table));
1738c2ecf20Sopenharmony_ci	return in_be32(&((struct epapr_spin_table *)spin_table)->addr_l);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
1778c2ecf20Sopenharmony_cistatic void wake_hw_thread(void *info)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	void fsl_secondary_thread_init(void);
1808c2ecf20Sopenharmony_ci	unsigned long inia;
1818c2ecf20Sopenharmony_ci	int cpu = *(const int *)info;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	inia = *(unsigned long *)fsl_secondary_thread_init;
1848c2ecf20Sopenharmony_ci	book3e_start_thread(cpu_thread_in_core(cpu), inia);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci#endif
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int smp_85xx_start_cpu(int cpu)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int ret = 0;
1918c2ecf20Sopenharmony_ci	struct device_node *np;
1928c2ecf20Sopenharmony_ci	const u64 *cpu_rel_addr;
1938c2ecf20Sopenharmony_ci	unsigned long flags;
1948c2ecf20Sopenharmony_ci	int ioremappable;
1958c2ecf20Sopenharmony_ci	int hw_cpu = get_hard_smp_processor_id(cpu);
1968c2ecf20Sopenharmony_ci	struct epapr_spin_table __iomem *spin_table;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	np = of_get_cpu_node(cpu, NULL);
1998c2ecf20Sopenharmony_ci	cpu_rel_addr = of_get_property(np, "cpu-release-addr", NULL);
2008c2ecf20Sopenharmony_ci	if (!cpu_rel_addr) {
2018c2ecf20Sopenharmony_ci		pr_err("No cpu-release-addr for cpu %d\n", cpu);
2028c2ecf20Sopenharmony_ci		return -ENOENT;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/*
2068c2ecf20Sopenharmony_ci	 * A secondary core could be in a spinloop in the bootpage
2078c2ecf20Sopenharmony_ci	 * (0xfffff000), somewhere in highmem, or somewhere in lowmem.
2088c2ecf20Sopenharmony_ci	 * The bootpage and highmem can be accessed via ioremap(), but
2098c2ecf20Sopenharmony_ci	 * we need to directly access the spinloop if its in lowmem.
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci	ioremappable = *cpu_rel_addr > virt_to_phys(high_memory);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* Map the spin table */
2148c2ecf20Sopenharmony_ci	if (ioremappable)
2158c2ecf20Sopenharmony_ci		spin_table = ioremap_coherent(*cpu_rel_addr,
2168c2ecf20Sopenharmony_ci					      sizeof(struct epapr_spin_table));
2178c2ecf20Sopenharmony_ci	else
2188c2ecf20Sopenharmony_ci		spin_table = phys_to_virt(*cpu_rel_addr);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	local_irq_save(flags);
2218c2ecf20Sopenharmony_ci	hard_irq_disable();
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (qoriq_pm_ops && qoriq_pm_ops->cpu_up_prepare)
2248c2ecf20Sopenharmony_ci		qoriq_pm_ops->cpu_up_prepare(cpu);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* if cpu is not spinning, reset it */
2278c2ecf20Sopenharmony_ci	if (read_spin_table_addr_l(spin_table) != 1) {
2288c2ecf20Sopenharmony_ci		/*
2298c2ecf20Sopenharmony_ci		 * We don't set the BPTR register here since it already points
2308c2ecf20Sopenharmony_ci		 * to the boot page properly.
2318c2ecf20Sopenharmony_ci		 */
2328c2ecf20Sopenharmony_ci		mpic_reset_core(cpu);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		/*
2358c2ecf20Sopenharmony_ci		 * wait until core is ready...
2368c2ecf20Sopenharmony_ci		 * We need to invalidate the stale data, in case the boot
2378c2ecf20Sopenharmony_ci		 * loader uses a cache-inhibited spin table.
2388c2ecf20Sopenharmony_ci		 */
2398c2ecf20Sopenharmony_ci		if (!spin_event_timeout(
2408c2ecf20Sopenharmony_ci				read_spin_table_addr_l(spin_table) == 1,
2418c2ecf20Sopenharmony_ci				10000, 100)) {
2428c2ecf20Sopenharmony_ci			pr_err("timeout waiting for cpu %d to reset\n",
2438c2ecf20Sopenharmony_ci				hw_cpu);
2448c2ecf20Sopenharmony_ci			ret = -EAGAIN;
2458c2ecf20Sopenharmony_ci			goto err;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	flush_spin_table(spin_table);
2508c2ecf20Sopenharmony_ci	out_be32(&spin_table->pir, hw_cpu);
2518c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
2528c2ecf20Sopenharmony_ci	out_be64((u64 *)(&spin_table->addr_h),
2538c2ecf20Sopenharmony_ci		__pa(ppc_function_entry(generic_secondary_smp_init)));
2548c2ecf20Sopenharmony_ci#else
2558c2ecf20Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT
2568c2ecf20Sopenharmony_ci	/*
2578c2ecf20Sopenharmony_ci	 * We need also to write addr_h to spin table for systems
2588c2ecf20Sopenharmony_ci	 * in which their physical memory start address was configured
2598c2ecf20Sopenharmony_ci	 * to above 4G, otherwise the secondary core can not get
2608c2ecf20Sopenharmony_ci	 * correct entry to start from.
2618c2ecf20Sopenharmony_ci	 */
2628c2ecf20Sopenharmony_ci	out_be32(&spin_table->addr_h, __pa(__early_start) >> 32);
2638c2ecf20Sopenharmony_ci#endif
2648c2ecf20Sopenharmony_ci	out_be32(&spin_table->addr_l, __pa(__early_start));
2658c2ecf20Sopenharmony_ci#endif
2668c2ecf20Sopenharmony_ci	flush_spin_table(spin_table);
2678c2ecf20Sopenharmony_cierr:
2688c2ecf20Sopenharmony_ci	local_irq_restore(flags);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (ioremappable)
2718c2ecf20Sopenharmony_ci		iounmap(spin_table);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return ret;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int smp_85xx_kick_cpu(int nr)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int ret = 0;
2798c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
2808c2ecf20Sopenharmony_ci	int primary = nr;
2818c2ecf20Sopenharmony_ci#endif
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	WARN_ON(nr < 0 || nr >= num_possible_cpus());
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	pr_debug("kick CPU #%d\n", nr);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
2888c2ecf20Sopenharmony_ci	if (threads_per_core == 2) {
2898c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!cpu_has_feature(CPU_FTR_SMT)))
2908c2ecf20Sopenharmony_ci			return -ENOENT;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		booting_thread_hwid = cpu_thread_in_core(nr);
2938c2ecf20Sopenharmony_ci		primary = cpu_first_thread_sibling(nr);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		if (qoriq_pm_ops && qoriq_pm_ops->cpu_up_prepare)
2968c2ecf20Sopenharmony_ci			qoriq_pm_ops->cpu_up_prepare(nr);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		/*
2998c2ecf20Sopenharmony_ci		 * If either thread in the core is online, use it to start
3008c2ecf20Sopenharmony_ci		 * the other.
3018c2ecf20Sopenharmony_ci		 */
3028c2ecf20Sopenharmony_ci		if (cpu_online(primary)) {
3038c2ecf20Sopenharmony_ci			smp_call_function_single(primary,
3048c2ecf20Sopenharmony_ci					wake_hw_thread, &nr, 1);
3058c2ecf20Sopenharmony_ci			goto done;
3068c2ecf20Sopenharmony_ci		} else if (cpu_online(primary + 1)) {
3078c2ecf20Sopenharmony_ci			smp_call_function_single(primary + 1,
3088c2ecf20Sopenharmony_ci					wake_hw_thread, &nr, 1);
3098c2ecf20Sopenharmony_ci			goto done;
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		/*
3138c2ecf20Sopenharmony_ci		 * If getting here, it means both threads in the core are
3148c2ecf20Sopenharmony_ci		 * offline. So start the primary thread, then it will start
3158c2ecf20Sopenharmony_ci		 * the thread specified in booting_thread_hwid, the one
3168c2ecf20Sopenharmony_ci		 * corresponding to nr.
3178c2ecf20Sopenharmony_ci		 */
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	} else if (threads_per_core == 1) {
3208c2ecf20Sopenharmony_ci		/*
3218c2ecf20Sopenharmony_ci		 * If one core has only one thread, set booting_thread_hwid to
3228c2ecf20Sopenharmony_ci		 * an invalid value.
3238c2ecf20Sopenharmony_ci		 */
3248c2ecf20Sopenharmony_ci		booting_thread_hwid = INVALID_THREAD_HWID;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	} else if (threads_per_core > 2) {
3278c2ecf20Sopenharmony_ci		pr_err("Do not support more than 2 threads per CPU.");
3288c2ecf20Sopenharmony_ci		return -EINVAL;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	ret = smp_85xx_start_cpu(primary);
3328c2ecf20Sopenharmony_ci	if (ret)
3338c2ecf20Sopenharmony_ci		return ret;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cidone:
3368c2ecf20Sopenharmony_ci	paca_ptrs[nr]->cpu_start = 1;
3378c2ecf20Sopenharmony_ci	generic_set_cpu_up(nr);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return ret;
3408c2ecf20Sopenharmony_ci#else
3418c2ecf20Sopenharmony_ci	ret = smp_85xx_start_cpu(nr);
3428c2ecf20Sopenharmony_ci	if (ret)
3438c2ecf20Sopenharmony_ci		return ret;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	generic_set_cpu_up(nr);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return ret;
3488c2ecf20Sopenharmony_ci#endif
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistruct smp_ops_t smp_85xx_ops = {
3528c2ecf20Sopenharmony_ci	.cause_nmi_ipi = NULL,
3538c2ecf20Sopenharmony_ci	.kick_cpu = smp_85xx_kick_cpu,
3548c2ecf20Sopenharmony_ci	.cpu_bootable = smp_generic_cpu_bootable,
3558c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
3568c2ecf20Sopenharmony_ci	.cpu_disable	= generic_cpu_disable,
3578c2ecf20Sopenharmony_ci	.cpu_die	= generic_cpu_die,
3588c2ecf20Sopenharmony_ci#endif
3598c2ecf20Sopenharmony_ci#if defined(CONFIG_KEXEC_CORE) && !defined(CONFIG_PPC64)
3608c2ecf20Sopenharmony_ci	.give_timebase	= smp_generic_give_timebase,
3618c2ecf20Sopenharmony_ci	.take_timebase	= smp_generic_take_timebase,
3628c2ecf20Sopenharmony_ci#endif
3638c2ecf20Sopenharmony_ci};
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE
3668c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
3678c2ecf20Sopenharmony_ciatomic_t kexec_down_cpus = ATOMIC_INIT(0);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_civoid mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	local_irq_disable();
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (secondary) {
3748c2ecf20Sopenharmony_ci		cur_cpu_spec->cpu_down_flush();
3758c2ecf20Sopenharmony_ci		atomic_inc(&kexec_down_cpus);
3768c2ecf20Sopenharmony_ci		/* loop forever */
3778c2ecf20Sopenharmony_ci		while (1);
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void mpc85xx_smp_kexec_down(void *arg)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	if (ppc_md.kexec_cpu_down)
3848c2ecf20Sopenharmony_ci		ppc_md.kexec_cpu_down(0,1);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci#else
3878c2ecf20Sopenharmony_civoid mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
3908c2ecf20Sopenharmony_ci	int sibling = cpu_last_thread_sibling(cpu);
3918c2ecf20Sopenharmony_ci	bool notified = false;
3928c2ecf20Sopenharmony_ci	int disable_cpu;
3938c2ecf20Sopenharmony_ci	int disable_threadbit = 0;
3948c2ecf20Sopenharmony_ci	long start = mftb();
3958c2ecf20Sopenharmony_ci	long now;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	local_irq_disable();
3988c2ecf20Sopenharmony_ci	hard_irq_disable();
3998c2ecf20Sopenharmony_ci	mpic_teardown_this_cpu(secondary);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (cpu == crashing_cpu && cpu_thread_in_core(cpu) != 0) {
4028c2ecf20Sopenharmony_ci		/*
4038c2ecf20Sopenharmony_ci		 * We enter the crash kernel on whatever cpu crashed,
4048c2ecf20Sopenharmony_ci		 * even if it's a secondary thread.  If that's the case,
4058c2ecf20Sopenharmony_ci		 * disable the corresponding primary thread.
4068c2ecf20Sopenharmony_ci		 */
4078c2ecf20Sopenharmony_ci		disable_threadbit = 1;
4088c2ecf20Sopenharmony_ci		disable_cpu = cpu_first_thread_sibling(cpu);
4098c2ecf20Sopenharmony_ci	} else if (sibling != crashing_cpu &&
4108c2ecf20Sopenharmony_ci		   cpu_thread_in_core(cpu) == 0 &&
4118c2ecf20Sopenharmony_ci		   cpu_thread_in_core(sibling) != 0) {
4128c2ecf20Sopenharmony_ci		disable_threadbit = 2;
4138c2ecf20Sopenharmony_ci		disable_cpu = sibling;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (disable_threadbit) {
4178c2ecf20Sopenharmony_ci		while (paca_ptrs[disable_cpu]->kexec_state < KEXEC_STATE_REAL_MODE) {
4188c2ecf20Sopenharmony_ci			barrier();
4198c2ecf20Sopenharmony_ci			now = mftb();
4208c2ecf20Sopenharmony_ci			if (!notified && now - start > 1000000) {
4218c2ecf20Sopenharmony_ci				pr_info("%s/%d: waiting for cpu %d to enter KEXEC_STATE_REAL_MODE (%d)\n",
4228c2ecf20Sopenharmony_ci					__func__, smp_processor_id(),
4238c2ecf20Sopenharmony_ci					disable_cpu,
4248c2ecf20Sopenharmony_ci					paca_ptrs[disable_cpu]->kexec_state);
4258c2ecf20Sopenharmony_ci				notified = true;
4268c2ecf20Sopenharmony_ci			}
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		if (notified) {
4308c2ecf20Sopenharmony_ci			pr_info("%s: cpu %d done waiting\n",
4318c2ecf20Sopenharmony_ci				__func__, disable_cpu);
4328c2ecf20Sopenharmony_ci		}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		mtspr(SPRN_TENC, disable_threadbit);
4358c2ecf20Sopenharmony_ci		while (mfspr(SPRN_TENSR) & disable_threadbit)
4368c2ecf20Sopenharmony_ci			cpu_relax();
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci#endif
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic void mpc85xx_smp_machine_kexec(struct kimage *image)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
4448c2ecf20Sopenharmony_ci	int timeout = INT_MAX;
4458c2ecf20Sopenharmony_ci	int i, num_cpus = num_present_cpus();
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (image->type == KEXEC_TYPE_DEFAULT)
4488c2ecf20Sopenharmony_ci		smp_call_function(mpc85xx_smp_kexec_down, NULL, 0);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	while ( (atomic_read(&kexec_down_cpus) != (num_cpus - 1)) &&
4518c2ecf20Sopenharmony_ci		( timeout > 0 ) )
4528c2ecf20Sopenharmony_ci	{
4538c2ecf20Sopenharmony_ci		timeout--;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if ( !timeout )
4578c2ecf20Sopenharmony_ci		printk(KERN_ERR "Unable to bring down secondary cpu(s)");
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	for_each_online_cpu(i)
4608c2ecf20Sopenharmony_ci	{
4618c2ecf20Sopenharmony_ci		if ( i == smp_processor_id() ) continue;
4628c2ecf20Sopenharmony_ci		mpic_reset_core(i);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci#endif
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	default_machine_kexec(image);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci#endif /* CONFIG_KEXEC_CORE */
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic void smp_85xx_setup_cpu(int cpu_nr)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	mpic_setup_this_cpu();
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_civoid __init mpc85xx_smp_init(void)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct device_node *np;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	np = of_find_node_by_type(NULL, "open-pic");
4818c2ecf20Sopenharmony_ci	if (np) {
4828c2ecf20Sopenharmony_ci		smp_85xx_ops.probe = smp_mpic_probe;
4838c2ecf20Sopenharmony_ci		smp_85xx_ops.setup_cpu = smp_85xx_setup_cpu;
4848c2ecf20Sopenharmony_ci		smp_85xx_ops.message_pass = smp_mpic_message_pass;
4858c2ecf20Sopenharmony_ci	} else
4868c2ecf20Sopenharmony_ci		smp_85xx_ops.setup_cpu = NULL;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_DBELL)) {
4898c2ecf20Sopenharmony_ci		/*
4908c2ecf20Sopenharmony_ci		 * If left NULL, .message_pass defaults to
4918c2ecf20Sopenharmony_ci		 * smp_muxed_ipi_message_pass
4928c2ecf20Sopenharmony_ci		 */
4938c2ecf20Sopenharmony_ci		smp_85xx_ops.message_pass = NULL;
4948c2ecf20Sopenharmony_ci		smp_85xx_ops.cause_ipi = doorbell_global_ipi;
4958c2ecf20Sopenharmony_ci		smp_85xx_ops.probe = NULL;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci#ifdef CONFIG_FSL_CORENET_RCPM
4998c2ecf20Sopenharmony_ci	/* Assign a value to qoriq_pm_ops on PPC_E500MC */
5008c2ecf20Sopenharmony_ci	fsl_rcpm_init();
5018c2ecf20Sopenharmony_ci#else
5028c2ecf20Sopenharmony_ci	/* Assign a value to qoriq_pm_ops on !PPC_E500MC */
5038c2ecf20Sopenharmony_ci	mpc85xx_setup_pmc();
5048c2ecf20Sopenharmony_ci#endif
5058c2ecf20Sopenharmony_ci	if (qoriq_pm_ops) {
5068c2ecf20Sopenharmony_ci		smp_85xx_ops.give_timebase = mpc85xx_give_timebase;
5078c2ecf20Sopenharmony_ci		smp_85xx_ops.take_timebase = mpc85xx_take_timebase;
5088c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
5098c2ecf20Sopenharmony_ci		smp_85xx_ops.cpu_offline_self = smp_85xx_cpu_offline_self;
5108c2ecf20Sopenharmony_ci		smp_85xx_ops.cpu_die = qoriq_cpu_kill;
5118c2ecf20Sopenharmony_ci#endif
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci	smp_ops = &smp_85xx_ops;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE
5168c2ecf20Sopenharmony_ci	ppc_md.kexec_cpu_down = mpc85xx_smp_kexec_cpu_down;
5178c2ecf20Sopenharmony_ci	ppc_md.machine_kexec = mpc85xx_smp_machine_kexec;
5188c2ecf20Sopenharmony_ci#endif
5198c2ecf20Sopenharmony_ci}
520