18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2014 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright (C) 2014 ZTE Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci#include <linux/smp.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 178c2ecf20Sopenharmony_ci#include <asm/cp15.h> 188c2ecf20Sopenharmony_ci#include <asm/fncpy.h> 198c2ecf20Sopenharmony_ci#include <asm/proc-fns.h> 208c2ecf20Sopenharmony_ci#include <asm/smp_scu.h> 218c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "core.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define AON_SYS_CTRL_RESERVED1 0xa8 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define BUS_MATRIX_REMAP_CONFIG 0x00 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define PCU_CPU0_CTRL 0x00 308c2ecf20Sopenharmony_ci#define PCU_CPU1_CTRL 0x04 318c2ecf20Sopenharmony_ci#define PCU_CPU1_ST 0x0c 328c2ecf20Sopenharmony_ci#define PCU_GLOBAL_CTRL 0x14 338c2ecf20Sopenharmony_ci#define PCU_EXPEND_CONTROL 0x34 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define ZX_IRAM_BASE 0x00200000 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void __iomem *pcu_base; 388c2ecf20Sopenharmony_cistatic void __iomem *matrix_base; 398c2ecf20Sopenharmony_cistatic void __iomem *scu_base; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid __init zx_smp_prepare_cpus(unsigned int max_cpus) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct device_node *np; 448c2ecf20Sopenharmony_ci unsigned long base = 0; 458c2ecf20Sopenharmony_ci void __iomem *aonsysctrl_base; 468c2ecf20Sopenharmony_ci void __iomem *sys_iram; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci base = scu_a9_get_base(); 498c2ecf20Sopenharmony_ci scu_base = ioremap(base, SZ_256); 508c2ecf20Sopenharmony_ci if (!scu_base) { 518c2ecf20Sopenharmony_ci pr_err("%s: failed to map scu\n", __func__); 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci scu_enable(scu_base); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "zte,sysctrl"); 588c2ecf20Sopenharmony_ci if (!np) { 598c2ecf20Sopenharmony_ci pr_err("%s: failed to find sysctrl node\n", __func__); 608c2ecf20Sopenharmony_ci return; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci aonsysctrl_base = of_iomap(np, 0); 648c2ecf20Sopenharmony_ci if (!aonsysctrl_base) { 658c2ecf20Sopenharmony_ci pr_err("%s: failed to map aonsysctrl\n", __func__); 668c2ecf20Sopenharmony_ci of_node_put(np); 678c2ecf20Sopenharmony_ci return; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * Write the address of secondary startup into the 728c2ecf20Sopenharmony_ci * system-wide flags register. The BootMonitor waits 738c2ecf20Sopenharmony_ci * until it receives a soft interrupt, and then the 748c2ecf20Sopenharmony_ci * secondary CPU branches to this address. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci __raw_writel(__pa_symbol(zx_secondary_startup), 778c2ecf20Sopenharmony_ci aonsysctrl_base + AON_SYS_CTRL_RESERVED1); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci iounmap(aonsysctrl_base); 808c2ecf20Sopenharmony_ci of_node_put(np); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "zte,zx296702-pcu"); 838c2ecf20Sopenharmony_ci pcu_base = of_iomap(np, 0); 848c2ecf20Sopenharmony_ci of_node_put(np); 858c2ecf20Sopenharmony_ci WARN_ON(!pcu_base); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "zte,zx-bus-matrix"); 888c2ecf20Sopenharmony_ci matrix_base = of_iomap(np, 0); 898c2ecf20Sopenharmony_ci of_node_put(np); 908c2ecf20Sopenharmony_ci WARN_ON(!matrix_base); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Map the first 4 KB IRAM for suspend usage */ 938c2ecf20Sopenharmony_ci sys_iram = __arm_ioremap_exec(ZX_IRAM_BASE, PAGE_SIZE, false); 948c2ecf20Sopenharmony_ci zx_secondary_startup_pa = __pa_symbol(zx_secondary_startup); 958c2ecf20Sopenharmony_ci fncpy(sys_iram, &zx_resume_jump, zx_suspend_iram_sz); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int zx_boot_secondary(unsigned int cpu, struct task_struct *idle) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci static bool first_boot = true; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (first_boot) { 1038c2ecf20Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 1048c2ecf20Sopenharmony_ci first_boot = false; 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Swap the base address mapping between IRAM and IROM */ 1098c2ecf20Sopenharmony_ci writel_relaxed(0x1, matrix_base + BUS_MATRIX_REMAP_CONFIG); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Power on CPU1 */ 1128c2ecf20Sopenharmony_ci writel_relaxed(0x0, pcu_base + PCU_CPU1_CTRL); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Wait for power on ack */ 1158c2ecf20Sopenharmony_ci while (readl_relaxed(pcu_base + PCU_CPU1_ST) & 0x4) 1168c2ecf20Sopenharmony_ci cpu_relax(); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Swap back the mapping of IRAM and IROM */ 1198c2ecf20Sopenharmony_ci writel_relaxed(0x0, matrix_base + BUS_MATRIX_REMAP_CONFIG); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1258c2ecf20Sopenharmony_cistatic inline void cpu_enter_lowpower(void) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci unsigned int v; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci asm volatile( 1308c2ecf20Sopenharmony_ci "mcr p15, 0, %1, c7, c5, 0\n" 1318c2ecf20Sopenharmony_ci " mcr p15, 0, %1, c7, c10, 4\n" 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * Turn off coherency 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci " mrc p15, 0, %0, c1, c0, 1\n" 1368c2ecf20Sopenharmony_ci " bic %0, %0, %3\n" 1378c2ecf20Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 1\n" 1388c2ecf20Sopenharmony_ci " mrc p15, 0, %0, c1, c0, 0\n" 1398c2ecf20Sopenharmony_ci " bic %0, %0, %2\n" 1408c2ecf20Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 0\n" 1418c2ecf20Sopenharmony_ci : "=&r" (v) 1428c2ecf20Sopenharmony_ci : "r" (0), "Ir" (CR_C), "Ir" (0x40) 1438c2ecf20Sopenharmony_ci : "cc"); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int zx_cpu_kill(unsigned int cpu) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(2000); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci writel_relaxed(0x2, pcu_base + PCU_CPU1_CTRL); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci while ((readl_relaxed(pcu_base + PCU_CPU1_ST) & 0x3) != 0x0) { 1538c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 1548c2ecf20Sopenharmony_ci pr_err("*** cpu1 poweroff timeout\n"); 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci return 1; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void zx_cpu_die(unsigned int cpu) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci scu_power_mode(scu_base, SCU_PM_POWEROFF); 1648c2ecf20Sopenharmony_ci cpu_enter_lowpower(); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci while (1) 1678c2ecf20Sopenharmony_ci cpu_do_idle(); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void zx_secondary_init(unsigned int cpu) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci scu_power_mode(scu_base, SCU_PM_NORMAL); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const struct smp_operations zx_smp_ops __initconst = { 1778c2ecf20Sopenharmony_ci .smp_prepare_cpus = zx_smp_prepare_cpus, 1788c2ecf20Sopenharmony_ci .smp_secondary_init = zx_secondary_init, 1798c2ecf20Sopenharmony_ci .smp_boot_secondary = zx_boot_secondary, 1808c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1818c2ecf20Sopenharmony_ci .cpu_kill = zx_cpu_kill, 1828c2ecf20Sopenharmony_ci .cpu_die = zx_cpu_die, 1838c2ecf20Sopenharmony_ci#endif 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(zx_smp, "zte,zx296702-smp", &zx_smp_ops); 187