1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 3 // http://www.samsung.com 4 // 5 // Cloned from linux/arch/arm/mach-vexpress/platsmp.c 6 // 7 // Copyright (C) 2002 ARM Ltd. 8 // All Rights Reserved 9 10 #include <linux/init.h> 11 #include <linux/errno.h> 12 #include <linux/delay.h> 13 #include <linux/jiffies.h> 14 #include <linux/smp.h> 15 #include <linux/io.h> 16 #include <linux/of_address.h> 17 #include <linux/soc/samsung/exynos-regs-pmu.h> 18 19 #include <asm/cacheflush.h> 20 #include <asm/cp15.h> 21 #include <asm/smp_plat.h> 22 #include <asm/smp_scu.h> 23 #include <asm/firmware.h> 24 25 #include "common.h" 26 27 extern void exynos4_secondary_startup(void); 28 29 /* XXX exynos_pen_release is cargo culted code - DO NOT COPY XXX */ 30 volatile int exynos_pen_release = -1; 31 32 #ifdef CONFIG_HOTPLUG_CPU 33 static inline void cpu_leave_lowpower(u32 core_id) 34 { 35 unsigned int v; 36 37 asm volatile( 38 "mrc p15, 0, %0, c1, c0, 0\n" 39 " orr %0, %0, %1\n" 40 " mcr p15, 0, %0, c1, c0, 0\n" 41 " mrc p15, 0, %0, c1, c0, 1\n" 42 " orr %0, %0, %2\n" 43 " mcr p15, 0, %0, c1, c0, 1\n" 44 : "=&r" (v) 45 : "Ir" (CR_C), "Ir" (0x40) 46 : "cc"); 47 } 48 49 static inline void platform_do_lowpower(unsigned int cpu, int *spurious) 50 { 51 u32 mpidr = cpu_logical_map(cpu); 52 u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); 53 54 for (;;) { 55 56 /* Turn the CPU off on next WFI instruction. */ 57 exynos_cpu_power_down(core_id); 58 59 wfi(); 60 61 if (exynos_pen_release == core_id) { 62 /* 63 * OK, proper wakeup, we're done 64 */ 65 break; 66 } 67 68 /* 69 * Getting here, means that we have come out of WFI without 70 * having been woken up - this shouldn't happen 71 * 72 * Just note it happening - when we're woken, we can report 73 * its occurrence. 74 */ 75 (*spurious)++; 76 } 77 } 78 #endif /* CONFIG_HOTPLUG_CPU */ 79 80 /** 81 * exynos_cpu_power_down() - power down the specified cpu 82 * @cpu: the cpu to power down 83 * 84 * Power down the specified cpu. The sequence must be finished by a 85 * call to cpu_do_idle() 86 */ 87 void exynos_cpu_power_down(int cpu) 88 { 89 u32 core_conf; 90 91 if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) { 92 /* 93 * Bypass power down for CPU0 during suspend. Check for 94 * the SYS_PWR_REG value to decide if we are suspending 95 * the system. 96 */ 97 int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG); 98 99 if (!(val & S5P_CORE_LOCAL_PWR_EN)) 100 return; 101 } 102 103 core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu)); 104 core_conf &= ~S5P_CORE_LOCAL_PWR_EN; 105 pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu)); 106 } 107 108 /** 109 * exynos_cpu_power_up() - power up the specified cpu 110 * @cpu: the cpu to power up 111 * 112 * Power up the specified cpu 113 */ 114 void exynos_cpu_power_up(int cpu) 115 { 116 u32 core_conf = S5P_CORE_LOCAL_PWR_EN; 117 118 if (soc_is_exynos3250()) 119 core_conf |= S5P_CORE_AUTOWAKEUP_EN; 120 121 pmu_raw_writel(core_conf, 122 EXYNOS_ARM_CORE_CONFIGURATION(cpu)); 123 } 124 125 /** 126 * exynos_cpu_power_state() - returns the power state of the cpu 127 * @cpu: the cpu to retrieve the power state from 128 */ 129 int exynos_cpu_power_state(int cpu) 130 { 131 return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) & 132 S5P_CORE_LOCAL_PWR_EN); 133 } 134 135 /** 136 * exynos_cluster_power_down() - power down the specified cluster 137 * @cluster: the cluster to power down 138 */ 139 void exynos_cluster_power_down(int cluster) 140 { 141 pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster)); 142 } 143 144 /** 145 * exynos_cluster_power_up() - power up the specified cluster 146 * @cluster: the cluster to power up 147 */ 148 void exynos_cluster_power_up(int cluster) 149 { 150 pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN, 151 EXYNOS_COMMON_CONFIGURATION(cluster)); 152 } 153 154 /** 155 * exynos_cluster_power_state() - returns the power state of the cluster 156 * @cluster: the cluster to retrieve the power state from 157 * 158 */ 159 int exynos_cluster_power_state(int cluster) 160 { 161 return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) & 162 S5P_CORE_LOCAL_PWR_EN); 163 } 164 165 /** 166 * exynos_scu_enable() - enables SCU for Cortex-A9 based system 167 */ 168 void exynos_scu_enable(void) 169 { 170 struct device_node *np; 171 static void __iomem *scu_base; 172 173 if (!scu_base) { 174 np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 175 if (np) { 176 scu_base = of_iomap(np, 0); 177 of_node_put(np); 178 } else { 179 scu_base = ioremap(scu_a9_get_base(), SZ_4K); 180 } 181 } 182 scu_enable(scu_base); 183 } 184 185 static void __iomem *cpu_boot_reg_base(void) 186 { 187 if (soc_is_exynos4210() && exynos_rev() == EXYNOS4210_REV_1_1) 188 return pmu_base_addr + S5P_INFORM5; 189 return sysram_base_addr; 190 } 191 192 static inline void __iomem *cpu_boot_reg(int cpu) 193 { 194 void __iomem *boot_reg; 195 196 boot_reg = cpu_boot_reg_base(); 197 if (!boot_reg) 198 return IOMEM_ERR_PTR(-ENODEV); 199 if (soc_is_exynos4412()) 200 boot_reg += 4*cpu; 201 else if (soc_is_exynos5420() || soc_is_exynos5800()) 202 boot_reg += 4; 203 return boot_reg; 204 } 205 206 /* 207 * Set wake up by local power mode and execute software reset for given core. 208 * 209 * Currently this is needed only when booting secondary CPU on Exynos3250. 210 */ 211 void exynos_core_restart(u32 core_id) 212 { 213 unsigned int timeout = 16; 214 u32 val; 215 216 if (!soc_is_exynos3250()) 217 return; 218 219 while (timeout && !pmu_raw_readl(S5P_PMU_SPARE2)) { 220 timeout--; 221 udelay(10); 222 } 223 if (timeout == 0) { 224 pr_err("cpu core %u restart failed\n", core_id); 225 return; 226 } 227 udelay(10); 228 229 val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id)); 230 val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG; 231 pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id)); 232 233 pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET); 234 } 235 236 /* 237 * XXX CARGO CULTED CODE - DO NOT COPY XXX 238 * 239 * Write exynos_pen_release in a way that is guaranteed to be visible to 240 * all observers, irrespective of whether they're taking part in coherency 241 * or not. This is necessary for the hotplug code to work reliably. 242 */ 243 static void exynos_write_pen_release(int val) 244 { 245 exynos_pen_release = val; 246 smp_wmb(); 247 sync_cache_w(&exynos_pen_release); 248 } 249 250 static DEFINE_SPINLOCK(boot_lock); 251 252 static void exynos_secondary_init(unsigned int cpu) 253 { 254 /* 255 * let the primary processor know we're out of the 256 * pen, then head off into the C entry point 257 */ 258 exynos_write_pen_release(-1); 259 260 /* 261 * Synchronise with the boot thread. 262 */ 263 spin_lock(&boot_lock); 264 spin_unlock(&boot_lock); 265 } 266 267 int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr) 268 { 269 int ret; 270 271 /* 272 * Try to set boot address using firmware first 273 * and fall back to boot register if it fails. 274 */ 275 ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr); 276 if (ret && ret != -ENOSYS) 277 goto fail; 278 if (ret == -ENOSYS) { 279 void __iomem *boot_reg = cpu_boot_reg(core_id); 280 281 if (IS_ERR(boot_reg)) { 282 ret = PTR_ERR(boot_reg); 283 goto fail; 284 } 285 writel_relaxed(boot_addr, boot_reg); 286 ret = 0; 287 } 288 fail: 289 return ret; 290 } 291 292 int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr) 293 { 294 int ret; 295 296 /* 297 * Try to get boot address using firmware first 298 * and fall back to boot register if it fails. 299 */ 300 ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr); 301 if (ret && ret != -ENOSYS) 302 goto fail; 303 if (ret == -ENOSYS) { 304 void __iomem *boot_reg = cpu_boot_reg(core_id); 305 306 if (IS_ERR(boot_reg)) { 307 ret = PTR_ERR(boot_reg); 308 goto fail; 309 } 310 *boot_addr = readl_relaxed(boot_reg); 311 ret = 0; 312 } 313 fail: 314 return ret; 315 } 316 317 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) 318 { 319 unsigned long timeout; 320 u32 mpidr = cpu_logical_map(cpu); 321 u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); 322 int ret = -ENOSYS; 323 324 /* 325 * Set synchronisation state between this boot processor 326 * and the secondary one 327 */ 328 spin_lock(&boot_lock); 329 330 /* 331 * The secondary processor is waiting to be released from 332 * the holding pen - release it, then wait for it to flag 333 * that it has been released by resetting exynos_pen_release. 334 * 335 * Note that "exynos_pen_release" is the hardware CPU core ID, whereas 336 * "cpu" is Linux's internal ID. 337 */ 338 exynos_write_pen_release(core_id); 339 340 if (!exynos_cpu_power_state(core_id)) { 341 exynos_cpu_power_up(core_id); 342 timeout = 10; 343 344 /* wait max 10 ms until cpu1 is on */ 345 while (exynos_cpu_power_state(core_id) 346 != S5P_CORE_LOCAL_PWR_EN) { 347 if (timeout == 0) 348 break; 349 timeout--; 350 mdelay(1); 351 } 352 353 if (timeout == 0) { 354 printk(KERN_ERR "cpu1 power enable failed"); 355 spin_unlock(&boot_lock); 356 return -ETIMEDOUT; 357 } 358 } 359 360 exynos_core_restart(core_id); 361 362 /* 363 * Send the secondary CPU a soft interrupt, thereby causing 364 * the boot monitor to read the system wide flags register, 365 * and branch to the address found there. 366 */ 367 368 timeout = jiffies + (1 * HZ); 369 while (time_before(jiffies, timeout)) { 370 unsigned long boot_addr; 371 372 smp_rmb(); 373 374 boot_addr = __pa_symbol(exynos4_secondary_startup); 375 376 ret = exynos_set_boot_addr(core_id, boot_addr); 377 if (ret) 378 goto fail; 379 380 call_firmware_op(cpu_boot, core_id); 381 382 if (soc_is_exynos3250()) 383 dsb_sev(); 384 else 385 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 386 387 if (exynos_pen_release == -1) 388 break; 389 390 udelay(10); 391 } 392 393 if (exynos_pen_release != -1) 394 ret = -ETIMEDOUT; 395 396 /* 397 * now the secondary core is starting up let it run its 398 * calibrations, then wait for it to finish 399 */ 400 fail: 401 spin_unlock(&boot_lock); 402 403 return exynos_pen_release != -1 ? ret : 0; 404 } 405 406 static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) 407 { 408 exynos_sysram_init(); 409 410 exynos_set_delayed_reset_assertion(true); 411 412 if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) 413 exynos_scu_enable(); 414 } 415 416 #ifdef CONFIG_HOTPLUG_CPU 417 /* 418 * platform-specific code to shutdown a CPU 419 * 420 * Called with IRQs disabled 421 */ 422 static void exynos_cpu_die(unsigned int cpu) 423 { 424 int spurious = 0; 425 u32 mpidr = cpu_logical_map(cpu); 426 u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); 427 428 v7_exit_coherency_flush(louis); 429 430 platform_do_lowpower(cpu, &spurious); 431 432 /* 433 * bring this CPU back into the world of cache 434 * coherency, and then restore interrupts 435 */ 436 cpu_leave_lowpower(core_id); 437 438 if (spurious) 439 pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); 440 } 441 #endif /* CONFIG_HOTPLUG_CPU */ 442 443 const struct smp_operations exynos_smp_ops __initconst = { 444 .smp_prepare_cpus = exynos_smp_prepare_cpus, 445 .smp_secondary_init = exynos_secondary_init, 446 .smp_boot_secondary = exynos_boot_secondary, 447 #ifdef CONFIG_HOTPLUG_CPU 448 .cpu_die = exynos_cpu_die, 449 #endif 450 }; 451
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.