162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 362306a36Sopenharmony_ci// http://www.samsung.com 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Cloned from linux/arch/arm/mach-vexpress/platsmp.c 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Copyright (C) 2002 ARM Ltd. 862306a36Sopenharmony_ci// All Rights Reserved 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/jiffies.h> 1462306a36Sopenharmony_ci#include <linux/smp.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/soc/samsung/exynos-regs-pmu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/cacheflush.h> 2062306a36Sopenharmony_ci#include <asm/cp15.h> 2162306a36Sopenharmony_ci#include <asm/smp_plat.h> 2262306a36Sopenharmony_ci#include <asm/smp_scu.h> 2362306a36Sopenharmony_ci#include <asm/firmware.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "common.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciextern void exynos4_secondary_startup(void); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* XXX exynos_pen_release is cargo culted code - DO NOT COPY XXX */ 3062306a36Sopenharmony_civolatile int exynos_pen_release = -1; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3362306a36Sopenharmony_cistatic inline void cpu_leave_lowpower(u32 core_id) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned int v; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci asm volatile( 3862306a36Sopenharmony_ci "mrc p15, 0, %0, c1, c0, 0\n" 3962306a36Sopenharmony_ci " orr %0, %0, %1\n" 4062306a36Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 0\n" 4162306a36Sopenharmony_ci " mrc p15, 0, %0, c1, c0, 1\n" 4262306a36Sopenharmony_ci " orr %0, %0, %2\n" 4362306a36Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 1\n" 4462306a36Sopenharmony_ci : "=&r" (v) 4562306a36Sopenharmony_ci : "Ir" (CR_C), "Ir" (0x40) 4662306a36Sopenharmony_ci : "cc"); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic inline void platform_do_lowpower(unsigned int cpu, int *spurious) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci u32 mpidr = cpu_logical_map(cpu); 5262306a36Sopenharmony_ci u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (;;) { 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Turn the CPU off on next WFI instruction. */ 5762306a36Sopenharmony_ci exynos_cpu_power_down(core_id); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci wfi(); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (exynos_pen_release == core_id) { 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * OK, proper wakeup, we're done 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Getting here, means that we have come out of WFI without 7062306a36Sopenharmony_ci * having been woken up - this shouldn't happen 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Just note it happening - when we're woken, we can report 7362306a36Sopenharmony_ci * its occurrence. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci (*spurious)++; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * exynos_cpu_power_down() - power down the specified cpu 8262306a36Sopenharmony_ci * @cpu: the cpu to power down 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Power down the specified cpu. The sequence must be finished by a 8562306a36Sopenharmony_ci * call to cpu_do_idle() 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_civoid exynos_cpu_power_down(int cpu) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci u32 core_conf; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) { 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * Bypass power down for CPU0 during suspend. Check for 9462306a36Sopenharmony_ci * the SYS_PWR_REG value to decide if we are suspending 9562306a36Sopenharmony_ci * the system. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!(val & S5P_CORE_LOCAL_PWR_EN)) 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu)); 10462306a36Sopenharmony_ci core_conf &= ~S5P_CORE_LOCAL_PWR_EN; 10562306a36Sopenharmony_ci pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu)); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * exynos_cpu_power_up() - power up the specified cpu 11062306a36Sopenharmony_ci * @cpu: the cpu to power up 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * Power up the specified cpu 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_civoid exynos_cpu_power_up(int cpu) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci u32 core_conf = S5P_CORE_LOCAL_PWR_EN; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (soc_is_exynos3250()) 11962306a36Sopenharmony_ci core_conf |= S5P_CORE_AUTOWAKEUP_EN; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci pmu_raw_writel(core_conf, 12262306a36Sopenharmony_ci EXYNOS_ARM_CORE_CONFIGURATION(cpu)); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/** 12662306a36Sopenharmony_ci * exynos_cpu_power_state() - returns the power state of the cpu 12762306a36Sopenharmony_ci * @cpu: the cpu to retrieve the power state from 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ciint exynos_cpu_power_state(int cpu) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) & 13262306a36Sopenharmony_ci S5P_CORE_LOCAL_PWR_EN); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * exynos_cluster_power_down() - power down the specified cluster 13762306a36Sopenharmony_ci * @cluster: the cluster to power down 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_civoid exynos_cluster_power_down(int cluster) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster)); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/** 14562306a36Sopenharmony_ci * exynos_cluster_power_up() - power up the specified cluster 14662306a36Sopenharmony_ci * @cluster: the cluster to power up 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_civoid exynos_cluster_power_up(int cluster) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN, 15162306a36Sopenharmony_ci EXYNOS_COMMON_CONFIGURATION(cluster)); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * exynos_cluster_power_state() - returns the power state of the cluster 15662306a36Sopenharmony_ci * @cluster: the cluster to retrieve the power state from 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ciint exynos_cluster_power_state(int cluster) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) & 16262306a36Sopenharmony_ci S5P_CORE_LOCAL_PWR_EN); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * exynos_scu_enable() - enables SCU for Cortex-A9 based system 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_civoid exynos_scu_enable(void) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct device_node *np; 17162306a36Sopenharmony_ci static void __iomem *scu_base; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!scu_base) { 17462306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 17562306a36Sopenharmony_ci if (np) { 17662306a36Sopenharmony_ci scu_base = of_iomap(np, 0); 17762306a36Sopenharmony_ci of_node_put(np); 17862306a36Sopenharmony_ci } else { 17962306a36Sopenharmony_ci scu_base = ioremap(scu_a9_get_base(), SZ_4K); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci scu_enable(scu_base); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void __iomem *cpu_boot_reg_base(void) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci if (soc_is_exynos4210() && exynos_rev() == EXYNOS4210_REV_1_1) 18862306a36Sopenharmony_ci return pmu_base_addr + S5P_INFORM5; 18962306a36Sopenharmony_ci return sysram_base_addr; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic inline void __iomem *cpu_boot_reg(int cpu) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci void __iomem *boot_reg; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci boot_reg = cpu_boot_reg_base(); 19762306a36Sopenharmony_ci if (!boot_reg) 19862306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENODEV); 19962306a36Sopenharmony_ci if (soc_is_exynos4412()) 20062306a36Sopenharmony_ci boot_reg += 4*cpu; 20162306a36Sopenharmony_ci else if (soc_is_exynos5420() || soc_is_exynos5800()) 20262306a36Sopenharmony_ci boot_reg += 4; 20362306a36Sopenharmony_ci return boot_reg; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * Set wake up by local power mode and execute software reset for given core. 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * Currently this is needed only when booting secondary CPU on Exynos3250. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_civoid exynos_core_restart(u32 core_id) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci unsigned int timeout = 16; 21462306a36Sopenharmony_ci u32 val; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!soc_is_exynos3250()) 21762306a36Sopenharmony_ci return; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci while (timeout && !pmu_raw_readl(S5P_PMU_SPARE2)) { 22062306a36Sopenharmony_ci timeout--; 22162306a36Sopenharmony_ci udelay(10); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci if (timeout == 0) { 22462306a36Sopenharmony_ci pr_err("cpu core %u restart failed\n", core_id); 22562306a36Sopenharmony_ci return; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci udelay(10); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id)); 23062306a36Sopenharmony_ci val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG; 23162306a36Sopenharmony_ci pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id)); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* 23762306a36Sopenharmony_ci * XXX CARGO CULTED CODE - DO NOT COPY XXX 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * Write exynos_pen_release in a way that is guaranteed to be visible to 24062306a36Sopenharmony_ci * all observers, irrespective of whether they're taking part in coherency 24162306a36Sopenharmony_ci * or not. This is necessary for the hotplug code to work reliably. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic void exynos_write_pen_release(int val) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci exynos_pen_release = val; 24662306a36Sopenharmony_ci smp_wmb(); 24762306a36Sopenharmony_ci sync_cache_w(&exynos_pen_release); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(boot_lock); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void exynos_secondary_init(unsigned int cpu) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * let the primary processor know we're out of the 25662306a36Sopenharmony_ci * pen, then head off into the C entry point 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci exynos_write_pen_release(-1); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * Synchronise with the boot thread. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci spin_lock(&boot_lock); 26462306a36Sopenharmony_ci spin_unlock(&boot_lock); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciint exynos_set_boot_addr(u32 core_id, unsigned long boot_addr) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci int ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * Try to set boot address using firmware first 27362306a36Sopenharmony_ci * and fall back to boot register if it fails. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr); 27662306a36Sopenharmony_ci if (ret && ret != -ENOSYS) 27762306a36Sopenharmony_ci goto fail; 27862306a36Sopenharmony_ci if (ret == -ENOSYS) { 27962306a36Sopenharmony_ci void __iomem *boot_reg = cpu_boot_reg(core_id); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (IS_ERR(boot_reg)) { 28262306a36Sopenharmony_ci ret = PTR_ERR(boot_reg); 28362306a36Sopenharmony_ci goto fail; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci writel_relaxed(boot_addr, boot_reg); 28662306a36Sopenharmony_ci ret = 0; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_cifail: 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciint exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * Try to get boot address using firmware first 29862306a36Sopenharmony_ci * and fall back to boot register if it fails. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr); 30162306a36Sopenharmony_ci if (ret && ret != -ENOSYS) 30262306a36Sopenharmony_ci goto fail; 30362306a36Sopenharmony_ci if (ret == -ENOSYS) { 30462306a36Sopenharmony_ci void __iomem *boot_reg = cpu_boot_reg(core_id); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (IS_ERR(boot_reg)) { 30762306a36Sopenharmony_ci ret = PTR_ERR(boot_reg); 30862306a36Sopenharmony_ci goto fail; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci *boot_addr = readl_relaxed(boot_reg); 31162306a36Sopenharmony_ci ret = 0; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_cifail: 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci unsigned long timeout; 32062306a36Sopenharmony_ci u32 mpidr = cpu_logical_map(cpu); 32162306a36Sopenharmony_ci u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); 32262306a36Sopenharmony_ci int ret = -ENOSYS; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* 32562306a36Sopenharmony_ci * Set synchronisation state between this boot processor 32662306a36Sopenharmony_ci * and the secondary one 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci spin_lock(&boot_lock); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * The secondary processor is waiting to be released from 33262306a36Sopenharmony_ci * the holding pen - release it, then wait for it to flag 33362306a36Sopenharmony_ci * that it has been released by resetting exynos_pen_release. 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Note that "exynos_pen_release" is the hardware CPU core ID, whereas 33662306a36Sopenharmony_ci * "cpu" is Linux's internal ID. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci exynos_write_pen_release(core_id); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!exynos_cpu_power_state(core_id)) { 34162306a36Sopenharmony_ci exynos_cpu_power_up(core_id); 34262306a36Sopenharmony_ci timeout = 10; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* wait max 10 ms until cpu1 is on */ 34562306a36Sopenharmony_ci while (exynos_cpu_power_state(core_id) 34662306a36Sopenharmony_ci != S5P_CORE_LOCAL_PWR_EN) { 34762306a36Sopenharmony_ci if (timeout == 0) 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci timeout--; 35062306a36Sopenharmony_ci mdelay(1); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (timeout == 0) { 35462306a36Sopenharmony_ci printk(KERN_ERR "cpu1 power enable failed"); 35562306a36Sopenharmony_ci spin_unlock(&boot_lock); 35662306a36Sopenharmony_ci return -ETIMEDOUT; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci exynos_core_restart(core_id); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* 36362306a36Sopenharmony_ci * Send the secondary CPU a soft interrupt, thereby causing 36462306a36Sopenharmony_ci * the boot monitor to read the system wide flags register, 36562306a36Sopenharmony_ci * and branch to the address found there. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci timeout = jiffies + (1 * HZ); 36962306a36Sopenharmony_ci while (time_before(jiffies, timeout)) { 37062306a36Sopenharmony_ci unsigned long boot_addr; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci smp_rmb(); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci boot_addr = __pa_symbol(exynos4_secondary_startup); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci ret = exynos_set_boot_addr(core_id, boot_addr); 37762306a36Sopenharmony_ci if (ret) 37862306a36Sopenharmony_ci goto fail; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci call_firmware_op(cpu_boot, core_id); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (soc_is_exynos3250()) 38362306a36Sopenharmony_ci dsb_sev(); 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (exynos_pen_release == -1) 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci udelay(10); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (exynos_pen_release != -1) 39462306a36Sopenharmony_ci ret = -ETIMEDOUT; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * now the secondary core is starting up let it run its 39862306a36Sopenharmony_ci * calibrations, then wait for it to finish 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_cifail: 40162306a36Sopenharmony_ci spin_unlock(&boot_lock); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return exynos_pen_release != -1 ? ret : 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void __init exynos_smp_prepare_cpus(unsigned int max_cpus) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci exynos_sysram_init(); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci exynos_set_delayed_reset_assertion(true); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) 41362306a36Sopenharmony_ci exynos_scu_enable(); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 41762306a36Sopenharmony_ci/* 41862306a36Sopenharmony_ci * platform-specific code to shutdown a CPU 41962306a36Sopenharmony_ci * 42062306a36Sopenharmony_ci * Called with IRQs disabled 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cistatic void exynos_cpu_die(unsigned int cpu) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci int spurious = 0; 42562306a36Sopenharmony_ci u32 mpidr = cpu_logical_map(cpu); 42662306a36Sopenharmony_ci u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci v7_exit_coherency_flush(louis); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci platform_do_lowpower(cpu, &spurious); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* 43362306a36Sopenharmony_ci * bring this CPU back into the world of cache 43462306a36Sopenharmony_ci * coherency, and then restore interrupts 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci cpu_leave_lowpower(core_id); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (spurious) 43962306a36Sopenharmony_ci pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciconst struct smp_operations exynos_smp_ops __initconst = { 44462306a36Sopenharmony_ci .smp_prepare_cpus = exynos_smp_prepare_cpus, 44562306a36Sopenharmony_ci .smp_secondary_init = exynos_secondary_init, 44662306a36Sopenharmony_ci .smp_boot_secondary = exynos_boot_secondary, 44762306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 44862306a36Sopenharmony_ci .cpu_die = exynos_cpu_die, 44962306a36Sopenharmony_ci#endif 45062306a36Sopenharmony_ci}; 451