18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright (c) 2013 Hisilicon Limited. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/cpu.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/of_address.h> 118c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 128c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 138c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 148c2ecf20Sopenharmony_ci#include "core.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* Sysctrl registers in Hi3620 SoC */ 178c2ecf20Sopenharmony_ci#define SCISOEN 0xc0 188c2ecf20Sopenharmony_ci#define SCISODIS 0xc4 198c2ecf20Sopenharmony_ci#define SCPERPWREN 0xd0 208c2ecf20Sopenharmony_ci#define SCPERPWRDIS 0xd4 218c2ecf20Sopenharmony_ci#define SCCPUCOREEN 0xf4 228c2ecf20Sopenharmony_ci#define SCCPUCOREDIS 0xf8 238c2ecf20Sopenharmony_ci#define SCPERCTRL0 0x200 248c2ecf20Sopenharmony_ci#define SCCPURSTEN 0x410 258c2ecf20Sopenharmony_ci#define SCCPURSTDIS 0x414 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * bit definition in SCISOEN/SCPERPWREN/... 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * CPU2_ISO_CTRL (1 << 5) 318c2ecf20Sopenharmony_ci * CPU3_ISO_CTRL (1 << 6) 328c2ecf20Sopenharmony_ci * ... 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#define CPU2_ISO_CTRL (1 << 5) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * bit definition in SCPERCTRL0 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * CPU0_WFI_MASK_CFG (1 << 28) 408c2ecf20Sopenharmony_ci * CPU1_WFI_MASK_CFG (1 << 29) 418c2ecf20Sopenharmony_ci * ... 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci#define CPU0_WFI_MASK_CFG (1 << 28) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * bit definition in SCCPURSTEN/... 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * CPU0_SRST_REQ_EN (1 << 0) 498c2ecf20Sopenharmony_ci * CPU1_SRST_REQ_EN (1 << 1) 508c2ecf20Sopenharmony_ci * ... 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci#define CPU0_HPM_SRST_REQ_EN (1 << 22) 538c2ecf20Sopenharmony_ci#define CPU0_DBG_SRST_REQ_EN (1 << 12) 548c2ecf20Sopenharmony_ci#define CPU0_NEON_SRST_REQ_EN (1 << 4) 558c2ecf20Sopenharmony_ci#define CPU0_SRST_REQ_EN (1 << 0) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define HIX5HD2_PERI_CRG20 0x50 588c2ecf20Sopenharmony_ci#define CRG20_CPU1_RESET (1 << 17) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define HIX5HD2_PERI_PMC0 0x1000 618c2ecf20Sopenharmony_ci#define PMC0_CPU1_WAIT_MTCOMS_ACK (1 << 8) 628c2ecf20Sopenharmony_ci#define PMC0_CPU1_PMC_ENABLE (1 << 7) 638c2ecf20Sopenharmony_ci#define PMC0_CPU1_POWERDOWN (1 << 3) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define HIP01_PERI9 0x50 668c2ecf20Sopenharmony_ci#define PERI9_CPU1_RESET (1 << 1) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cienum { 698c2ecf20Sopenharmony_ci HI3620_CTRL, 708c2ecf20Sopenharmony_ci ERROR_CTRL, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void __iomem *ctrl_base; 748c2ecf20Sopenharmony_cistatic int id; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void set_cpu_hi3620(int cpu, bool enable) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci u32 val = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (enable) { 818c2ecf20Sopenharmony_ci /* MTCMOS set */ 828c2ecf20Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) 838c2ecf20Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 848c2ecf20Sopenharmony_ci ctrl_base + SCPERPWREN); 858c2ecf20Sopenharmony_ci udelay(100); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Enable core */ 888c2ecf20Sopenharmony_ci writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREEN); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* unreset */ 918c2ecf20Sopenharmony_ci val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN 928c2ecf20Sopenharmony_ci | CPU0_SRST_REQ_EN; 938c2ecf20Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS); 948c2ecf20Sopenharmony_ci /* reset */ 958c2ecf20Sopenharmony_ci val |= CPU0_HPM_SRST_REQ_EN; 968c2ecf20Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* ISO disable */ 998c2ecf20Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) 1008c2ecf20Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 1018c2ecf20Sopenharmony_ci ctrl_base + SCISODIS); 1028c2ecf20Sopenharmony_ci udelay(1); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* WFI Mask */ 1058c2ecf20Sopenharmony_ci val = readl_relaxed(ctrl_base + SCPERCTRL0); 1068c2ecf20Sopenharmony_ci val &= ~(CPU0_WFI_MASK_CFG << cpu); 1078c2ecf20Sopenharmony_ci writel_relaxed(val, ctrl_base + SCPERCTRL0); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Unreset */ 1108c2ecf20Sopenharmony_ci val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN 1118c2ecf20Sopenharmony_ci | CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN; 1128c2ecf20Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS); 1138c2ecf20Sopenharmony_ci } else { 1148c2ecf20Sopenharmony_ci /* wfi mask */ 1158c2ecf20Sopenharmony_ci val = readl_relaxed(ctrl_base + SCPERCTRL0); 1168c2ecf20Sopenharmony_ci val |= (CPU0_WFI_MASK_CFG << cpu); 1178c2ecf20Sopenharmony_ci writel_relaxed(val, ctrl_base + SCPERCTRL0); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* disable core*/ 1208c2ecf20Sopenharmony_ci writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREDIS); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) { 1238c2ecf20Sopenharmony_ci /* iso enable */ 1248c2ecf20Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 1258c2ecf20Sopenharmony_ci ctrl_base + SCISOEN); 1268c2ecf20Sopenharmony_ci udelay(1); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* reset */ 1308c2ecf20Sopenharmony_ci val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN 1318c2ecf20Sopenharmony_ci | CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN; 1328c2ecf20Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) { 1358c2ecf20Sopenharmony_ci /* MTCMOS unset */ 1368c2ecf20Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 1378c2ecf20Sopenharmony_ci ctrl_base + SCPERPWRDIS); 1388c2ecf20Sopenharmony_ci udelay(100); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int hi3xxx_hotplug_init(void) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct device_node *node; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 1488c2ecf20Sopenharmony_ci if (!node) { 1498c2ecf20Sopenharmony_ci id = ERROR_CTRL; 1508c2ecf20Sopenharmony_ci return -ENOENT; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ctrl_base = of_iomap(node, 0); 1548c2ecf20Sopenharmony_ci of_node_put(node); 1558c2ecf20Sopenharmony_ci if (!ctrl_base) { 1568c2ecf20Sopenharmony_ci id = ERROR_CTRL; 1578c2ecf20Sopenharmony_ci return -ENOMEM; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci id = HI3620_CTRL; 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_civoid hi3xxx_set_cpu(int cpu, bool enable) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci if (!ctrl_base) { 1678c2ecf20Sopenharmony_ci if (hi3xxx_hotplug_init() < 0) 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (id == HI3620_CTRL) 1728c2ecf20Sopenharmony_ci set_cpu_hi3620(cpu, enable); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic bool hix5hd2_hotplug_init(void) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct device_node *np; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl"); 1808c2ecf20Sopenharmony_ci if (!np) 1818c2ecf20Sopenharmony_ci return false; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ctrl_base = of_iomap(np, 0); 1848c2ecf20Sopenharmony_ci of_node_put(np); 1858c2ecf20Sopenharmony_ci if (!ctrl_base) 1868c2ecf20Sopenharmony_ci return false; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return true; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_civoid hix5hd2_set_cpu(int cpu, bool enable) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci u32 val = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (!ctrl_base) 1968c2ecf20Sopenharmony_ci if (!hix5hd2_hotplug_init()) 1978c2ecf20Sopenharmony_ci BUG(); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (enable) { 2008c2ecf20Sopenharmony_ci /* power on cpu1 */ 2018c2ecf20Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0); 2028c2ecf20Sopenharmony_ci val &= ~(PMC0_CPU1_WAIT_MTCOMS_ACK | PMC0_CPU1_POWERDOWN); 2038c2ecf20Sopenharmony_ci val |= PMC0_CPU1_PMC_ENABLE; 2048c2ecf20Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0); 2058c2ecf20Sopenharmony_ci /* unreset */ 2068c2ecf20Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20); 2078c2ecf20Sopenharmony_ci val &= ~CRG20_CPU1_RESET; 2088c2ecf20Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20); 2098c2ecf20Sopenharmony_ci } else { 2108c2ecf20Sopenharmony_ci /* power down cpu1 */ 2118c2ecf20Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0); 2128c2ecf20Sopenharmony_ci val |= PMC0_CPU1_PMC_ENABLE | PMC0_CPU1_POWERDOWN; 2138c2ecf20Sopenharmony_ci val &= ~PMC0_CPU1_WAIT_MTCOMS_ACK; 2148c2ecf20Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* reset */ 2178c2ecf20Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20); 2188c2ecf20Sopenharmony_ci val |= CRG20_CPU1_RESET; 2198c2ecf20Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid hip01_set_cpu(int cpu, bool enable) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci unsigned int temp; 2268c2ecf20Sopenharmony_ci struct device_node *np; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!ctrl_base) { 2298c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl"); 2308c2ecf20Sopenharmony_ci BUG_ON(!np); 2318c2ecf20Sopenharmony_ci ctrl_base = of_iomap(np, 0); 2328c2ecf20Sopenharmony_ci of_node_put(np); 2338c2ecf20Sopenharmony_ci BUG_ON(!ctrl_base); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (enable) { 2378c2ecf20Sopenharmony_ci /* reset on CPU1 */ 2388c2ecf20Sopenharmony_ci temp = readl_relaxed(ctrl_base + HIP01_PERI9); 2398c2ecf20Sopenharmony_ci temp |= PERI9_CPU1_RESET; 2408c2ecf20Sopenharmony_ci writel_relaxed(temp, ctrl_base + HIP01_PERI9); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci udelay(50); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* unreset on CPU1 */ 2458c2ecf20Sopenharmony_ci temp = readl_relaxed(ctrl_base + HIP01_PERI9); 2468c2ecf20Sopenharmony_ci temp &= ~PERI9_CPU1_RESET; 2478c2ecf20Sopenharmony_ci writel_relaxed(temp, ctrl_base + HIP01_PERI9); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic inline void cpu_enter_lowpower(void) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci unsigned int v; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci flush_cache_all(); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* 2588c2ecf20Sopenharmony_ci * Turn off coherency and L1 D-cache 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci asm volatile( 2618c2ecf20Sopenharmony_ci " mrc p15, 0, %0, c1, c0, 1\n" 2628c2ecf20Sopenharmony_ci " bic %0, %0, #0x40\n" 2638c2ecf20Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 1\n" 2648c2ecf20Sopenharmony_ci " mrc p15, 0, %0, c1, c0, 0\n" 2658c2ecf20Sopenharmony_ci " bic %0, %0, #0x04\n" 2668c2ecf20Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 0\n" 2678c2ecf20Sopenharmony_ci : "=&r" (v) 2688c2ecf20Sopenharmony_ci : "r" (0) 2698c2ecf20Sopenharmony_ci : "cc"); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 2738c2ecf20Sopenharmony_civoid hi3xxx_cpu_die(unsigned int cpu) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci cpu_enter_lowpower(); 2768c2ecf20Sopenharmony_ci hi3xxx_set_cpu_jump(cpu, phys_to_virt(0)); 2778c2ecf20Sopenharmony_ci cpu_do_idle(); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* We should have never returned from idle */ 2808c2ecf20Sopenharmony_ci panic("cpu %d unexpectedly exit from shutdown\n", cpu); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciint hi3xxx_cpu_kill(unsigned int cpu) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(50); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci while (hi3xxx_get_cpu_jump(cpu)) 2888c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci hi3xxx_set_cpu(cpu, false); 2918c2ecf20Sopenharmony_ci return 1; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_civoid hix5hd2_cpu_die(unsigned int cpu) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci flush_cache_all(); 2978c2ecf20Sopenharmony_ci hix5hd2_set_cpu(cpu, false); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci#endif 300