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