162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd. 462306a36Sopenharmony_ci * Copyright (c) 2013 HiSilicon Limited. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/cpu.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/of_address.h> 1162306a36Sopenharmony_ci#include <asm/cacheflush.h> 1262306a36Sopenharmony_ci#include <asm/smp_plat.h> 1362306a36Sopenharmony_ci#include "core.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Sysctrl registers in Hi3620 SoC */ 1662306a36Sopenharmony_ci#define SCISOEN 0xc0 1762306a36Sopenharmony_ci#define SCISODIS 0xc4 1862306a36Sopenharmony_ci#define SCPERPWREN 0xd0 1962306a36Sopenharmony_ci#define SCPERPWRDIS 0xd4 2062306a36Sopenharmony_ci#define SCCPUCOREEN 0xf4 2162306a36Sopenharmony_ci#define SCCPUCOREDIS 0xf8 2262306a36Sopenharmony_ci#define SCPERCTRL0 0x200 2362306a36Sopenharmony_ci#define SCCPURSTEN 0x410 2462306a36Sopenharmony_ci#define SCCPURSTDIS 0x414 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * bit definition in SCISOEN/SCPERPWREN/... 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * CPU2_ISO_CTRL (1 << 5) 3062306a36Sopenharmony_ci * CPU3_ISO_CTRL (1 << 6) 3162306a36Sopenharmony_ci * ... 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#define CPU2_ISO_CTRL (1 << 5) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * bit definition in SCPERCTRL0 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * CPU0_WFI_MASK_CFG (1 << 28) 3962306a36Sopenharmony_ci * CPU1_WFI_MASK_CFG (1 << 29) 4062306a36Sopenharmony_ci * ... 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#define CPU0_WFI_MASK_CFG (1 << 28) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * bit definition in SCCPURSTEN/... 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * CPU0_SRST_REQ_EN (1 << 0) 4862306a36Sopenharmony_ci * CPU1_SRST_REQ_EN (1 << 1) 4962306a36Sopenharmony_ci * ... 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci#define CPU0_HPM_SRST_REQ_EN (1 << 22) 5262306a36Sopenharmony_ci#define CPU0_DBG_SRST_REQ_EN (1 << 12) 5362306a36Sopenharmony_ci#define CPU0_NEON_SRST_REQ_EN (1 << 4) 5462306a36Sopenharmony_ci#define CPU0_SRST_REQ_EN (1 << 0) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define HIX5HD2_PERI_CRG20 0x50 5762306a36Sopenharmony_ci#define CRG20_CPU1_RESET (1 << 17) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define HIX5HD2_PERI_PMC0 0x1000 6062306a36Sopenharmony_ci#define PMC0_CPU1_WAIT_MTCOMS_ACK (1 << 8) 6162306a36Sopenharmony_ci#define PMC0_CPU1_PMC_ENABLE (1 << 7) 6262306a36Sopenharmony_ci#define PMC0_CPU1_POWERDOWN (1 << 3) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define HIP01_PERI9 0x50 6562306a36Sopenharmony_ci#define PERI9_CPU1_RESET (1 << 1) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cienum { 6862306a36Sopenharmony_ci HI3620_CTRL, 6962306a36Sopenharmony_ci ERROR_CTRL, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void __iomem *ctrl_base; 7362306a36Sopenharmony_cistatic int id; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void set_cpu_hi3620(int cpu, bool enable) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci u32 val = 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (enable) { 8062306a36Sopenharmony_ci /* MTCMOS set */ 8162306a36Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) 8262306a36Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 8362306a36Sopenharmony_ci ctrl_base + SCPERPWREN); 8462306a36Sopenharmony_ci udelay(100); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Enable core */ 8762306a36Sopenharmony_ci writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREEN); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* unreset */ 9062306a36Sopenharmony_ci val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN 9162306a36Sopenharmony_ci | CPU0_SRST_REQ_EN; 9262306a36Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS); 9362306a36Sopenharmony_ci /* reset */ 9462306a36Sopenharmony_ci val |= CPU0_HPM_SRST_REQ_EN; 9562306a36Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* ISO disable */ 9862306a36Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) 9962306a36Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 10062306a36Sopenharmony_ci ctrl_base + SCISODIS); 10162306a36Sopenharmony_ci udelay(1); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* WFI Mask */ 10462306a36Sopenharmony_ci val = readl_relaxed(ctrl_base + SCPERCTRL0); 10562306a36Sopenharmony_ci val &= ~(CPU0_WFI_MASK_CFG << cpu); 10662306a36Sopenharmony_ci writel_relaxed(val, ctrl_base + SCPERCTRL0); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Unreset */ 10962306a36Sopenharmony_ci val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN 11062306a36Sopenharmony_ci | CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN; 11162306a36Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS); 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci /* wfi mask */ 11462306a36Sopenharmony_ci val = readl_relaxed(ctrl_base + SCPERCTRL0); 11562306a36Sopenharmony_ci val |= (CPU0_WFI_MASK_CFG << cpu); 11662306a36Sopenharmony_ci writel_relaxed(val, ctrl_base + SCPERCTRL0); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* disable core*/ 11962306a36Sopenharmony_ci writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREDIS); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) { 12262306a36Sopenharmony_ci /* iso enable */ 12362306a36Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 12462306a36Sopenharmony_ci ctrl_base + SCISOEN); 12562306a36Sopenharmony_ci udelay(1); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* reset */ 12962306a36Sopenharmony_ci val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN 13062306a36Sopenharmony_ci | CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN; 13162306a36Sopenharmony_ci writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if ((cpu == 2) || (cpu == 3)) { 13462306a36Sopenharmony_ci /* MTCMOS unset */ 13562306a36Sopenharmony_ci writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), 13662306a36Sopenharmony_ci ctrl_base + SCPERPWRDIS); 13762306a36Sopenharmony_ci udelay(100); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int hi3xxx_hotplug_init(void) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct device_node *node; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 14762306a36Sopenharmony_ci if (!node) { 14862306a36Sopenharmony_ci id = ERROR_CTRL; 14962306a36Sopenharmony_ci return -ENOENT; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ctrl_base = of_iomap(node, 0); 15362306a36Sopenharmony_ci of_node_put(node); 15462306a36Sopenharmony_ci if (!ctrl_base) { 15562306a36Sopenharmony_ci id = ERROR_CTRL; 15662306a36Sopenharmony_ci return -ENOMEM; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci id = HI3620_CTRL; 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_civoid hi3xxx_set_cpu(int cpu, bool enable) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci if (!ctrl_base) { 16662306a36Sopenharmony_ci if (hi3xxx_hotplug_init() < 0) 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (id == HI3620_CTRL) 17162306a36Sopenharmony_ci set_cpu_hi3620(cpu, enable); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic bool hix5hd2_hotplug_init(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct device_node *np; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl"); 17962306a36Sopenharmony_ci if (!np) 18062306a36Sopenharmony_ci return false; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ctrl_base = of_iomap(np, 0); 18362306a36Sopenharmony_ci of_node_put(np); 18462306a36Sopenharmony_ci if (!ctrl_base) 18562306a36Sopenharmony_ci return false; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return true; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid hix5hd2_set_cpu(int cpu, bool enable) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 val = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!ctrl_base) 19562306a36Sopenharmony_ci if (!hix5hd2_hotplug_init()) 19662306a36Sopenharmony_ci BUG(); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (enable) { 19962306a36Sopenharmony_ci /* power on cpu1 */ 20062306a36Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0); 20162306a36Sopenharmony_ci val &= ~(PMC0_CPU1_WAIT_MTCOMS_ACK | PMC0_CPU1_POWERDOWN); 20262306a36Sopenharmony_ci val |= PMC0_CPU1_PMC_ENABLE; 20362306a36Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0); 20462306a36Sopenharmony_ci /* unreset */ 20562306a36Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20); 20662306a36Sopenharmony_ci val &= ~CRG20_CPU1_RESET; 20762306a36Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20); 20862306a36Sopenharmony_ci } else { 20962306a36Sopenharmony_ci /* power down cpu1 */ 21062306a36Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0); 21162306a36Sopenharmony_ci val |= PMC0_CPU1_PMC_ENABLE | PMC0_CPU1_POWERDOWN; 21262306a36Sopenharmony_ci val &= ~PMC0_CPU1_WAIT_MTCOMS_ACK; 21362306a36Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* reset */ 21662306a36Sopenharmony_ci val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20); 21762306a36Sopenharmony_ci val |= CRG20_CPU1_RESET; 21862306a36Sopenharmony_ci writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_civoid hip01_set_cpu(int cpu, bool enable) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci unsigned int temp; 22562306a36Sopenharmony_ci struct device_node *np; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!ctrl_base) { 22862306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl"); 22962306a36Sopenharmony_ci BUG_ON(!np); 23062306a36Sopenharmony_ci ctrl_base = of_iomap(np, 0); 23162306a36Sopenharmony_ci of_node_put(np); 23262306a36Sopenharmony_ci BUG_ON(!ctrl_base); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (enable) { 23662306a36Sopenharmony_ci /* reset on CPU1 */ 23762306a36Sopenharmony_ci temp = readl_relaxed(ctrl_base + HIP01_PERI9); 23862306a36Sopenharmony_ci temp |= PERI9_CPU1_RESET; 23962306a36Sopenharmony_ci writel_relaxed(temp, ctrl_base + HIP01_PERI9); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci udelay(50); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* unreset on CPU1 */ 24462306a36Sopenharmony_ci temp = readl_relaxed(ctrl_base + HIP01_PERI9); 24562306a36Sopenharmony_ci temp &= ~PERI9_CPU1_RESET; 24662306a36Sopenharmony_ci writel_relaxed(temp, ctrl_base + HIP01_PERI9); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic inline void cpu_enter_lowpower(void) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci unsigned int v; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci flush_cache_all(); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Turn off coherency and L1 D-cache 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci asm volatile( 26062306a36Sopenharmony_ci " mrc p15, 0, %0, c1, c0, 1\n" 26162306a36Sopenharmony_ci " bic %0, %0, #0x40\n" 26262306a36Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 1\n" 26362306a36Sopenharmony_ci " mrc p15, 0, %0, c1, c0, 0\n" 26462306a36Sopenharmony_ci " bic %0, %0, #0x04\n" 26562306a36Sopenharmony_ci " mcr p15, 0, %0, c1, c0, 0\n" 26662306a36Sopenharmony_ci : "=&r" (v) 26762306a36Sopenharmony_ci : "r" (0) 26862306a36Sopenharmony_ci : "cc"); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 27262306a36Sopenharmony_civoid hi3xxx_cpu_die(unsigned int cpu) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci cpu_enter_lowpower(); 27562306a36Sopenharmony_ci hi3xxx_set_cpu_jump(cpu, phys_to_virt(0)); 27662306a36Sopenharmony_ci cpu_do_idle(); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* We should have never returned from idle */ 27962306a36Sopenharmony_ci panic("cpu %d unexpectedly exit from shutdown\n", cpu); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciint hi3xxx_cpu_kill(unsigned int cpu) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(50); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci while (hi3xxx_get_cpu_jump(cpu)) 28762306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci hi3xxx_set_cpu(cpu, false); 29062306a36Sopenharmony_ci return 1; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_civoid hix5hd2_cpu_die(unsigned int cpu) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci flush_cache_all(); 29662306a36Sopenharmony_ci hix5hd2_set_cpu(cpu, false); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci#endif 299