18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SMP support for Allwinner SoCs 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013 Maxime Ripard 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on code 98c2ecf20Sopenharmony_ci * Copyright (C) 2012-2013 Allwinner Ltd. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 128c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 138c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/memory.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_address.h> 228c2ecf20Sopenharmony_ci#include <linux/smp.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu) ((cpu) * 0x40 + 0x64) 258c2ecf20Sopenharmony_ci#define CPUCFG_CPU_RST_CTRL_REG(cpu) (((cpu) + 1) * 0x40) 268c2ecf20Sopenharmony_ci#define CPUCFG_CPU_CTRL_REG(cpu) (((cpu) + 1) * 0x40 + 0x04) 278c2ecf20Sopenharmony_ci#define CPUCFG_CPU_STATUS_REG(cpu) (((cpu) + 1) * 0x40 + 0x08) 288c2ecf20Sopenharmony_ci#define CPUCFG_GEN_CTRL_REG 0x184 298c2ecf20Sopenharmony_ci#define CPUCFG_PRIVATE0_REG 0x1a4 308c2ecf20Sopenharmony_ci#define CPUCFG_PRIVATE1_REG 0x1a8 318c2ecf20Sopenharmony_ci#define CPUCFG_DBG_CTL0_REG 0x1e0 328c2ecf20Sopenharmony_ci#define CPUCFG_DBG_CTL1_REG 0x1e4 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define PRCM_CPU_PWROFF_REG 0x100 358c2ecf20Sopenharmony_ci#define PRCM_CPU_PWR_CLAMP_REG(cpu) (((cpu) * 4) + 0x140) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void __iomem *cpucfg_membase; 388c2ecf20Sopenharmony_cistatic void __iomem *prcm_membase; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cpu_lock); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void __init sun6i_smp_prepare_cpus(unsigned int max_cpus) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct device_node *node; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm"); 478c2ecf20Sopenharmony_ci if (!node) { 488c2ecf20Sopenharmony_ci pr_err("Missing A31 PRCM node in the device tree\n"); 498c2ecf20Sopenharmony_ci return; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci prcm_membase = of_iomap(node, 0); 538c2ecf20Sopenharmony_ci of_node_put(node); 548c2ecf20Sopenharmony_ci if (!prcm_membase) { 558c2ecf20Sopenharmony_ci pr_err("Couldn't map A31 PRCM registers\n"); 568c2ecf20Sopenharmony_ci return; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, 608c2ecf20Sopenharmony_ci "allwinner,sun6i-a31-cpuconfig"); 618c2ecf20Sopenharmony_ci if (!node) { 628c2ecf20Sopenharmony_ci pr_err("Missing A31 CPU config node in the device tree\n"); 638c2ecf20Sopenharmony_ci return; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci cpucfg_membase = of_iomap(node, 0); 678c2ecf20Sopenharmony_ci of_node_put(node); 688c2ecf20Sopenharmony_ci if (!cpucfg_membase) 698c2ecf20Sopenharmony_ci pr_err("Couldn't map A31 CPU config registers\n"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int sun6i_smp_boot_secondary(unsigned int cpu, 748c2ecf20Sopenharmony_ci struct task_struct *idle) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u32 reg; 778c2ecf20Sopenharmony_ci int i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (!(prcm_membase && cpucfg_membase)) 808c2ecf20Sopenharmony_ci return -EFAULT; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci spin_lock(&cpu_lock); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Set CPU boot address */ 858c2ecf20Sopenharmony_ci writel(__pa_symbol(secondary_startup), 868c2ecf20Sopenharmony_ci cpucfg_membase + CPUCFG_PRIVATE0_REG); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Assert the CPU core in reset */ 898c2ecf20Sopenharmony_ci writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu)); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Assert the L1 cache in reset */ 928c2ecf20Sopenharmony_ci reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG); 938c2ecf20Sopenharmony_ci writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Disable external debug access */ 968c2ecf20Sopenharmony_ci reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG); 978c2ecf20Sopenharmony_ci writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Power up the CPU */ 1008c2ecf20Sopenharmony_ci for (i = 0; i <= 8; i++) 1018c2ecf20Sopenharmony_ci writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu)); 1028c2ecf20Sopenharmony_ci mdelay(10); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Clear CPU power-off gating */ 1058c2ecf20Sopenharmony_ci reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG); 1068c2ecf20Sopenharmony_ci writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG); 1078c2ecf20Sopenharmony_ci mdelay(1); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Deassert the CPU core reset */ 1108c2ecf20Sopenharmony_ci writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Enable back the external debug accesses */ 1138c2ecf20Sopenharmony_ci reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG); 1148c2ecf20Sopenharmony_ci writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci spin_unlock(&cpu_lock); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic const struct smp_operations sun6i_smp_ops __initconst = { 1228c2ecf20Sopenharmony_ci .smp_prepare_cpus = sun6i_smp_prepare_cpus, 1238c2ecf20Sopenharmony_ci .smp_boot_secondary = sun6i_smp_boot_secondary, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(sun6i_a31_smp, "allwinner,sun6i-a31", &sun6i_smp_ops); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void __init sun8i_smp_prepare_cpus(unsigned int max_cpus) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct device_node *node; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "allwinner,sun8i-a23-prcm"); 1328c2ecf20Sopenharmony_ci if (!node) { 1338c2ecf20Sopenharmony_ci pr_err("Missing A23 PRCM node in the device tree\n"); 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci prcm_membase = of_iomap(node, 0); 1388c2ecf20Sopenharmony_ci of_node_put(node); 1398c2ecf20Sopenharmony_ci if (!prcm_membase) { 1408c2ecf20Sopenharmony_ci pr_err("Couldn't map A23 PRCM registers\n"); 1418c2ecf20Sopenharmony_ci return; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, 1458c2ecf20Sopenharmony_ci "allwinner,sun8i-a23-cpuconfig"); 1468c2ecf20Sopenharmony_ci if (!node) { 1478c2ecf20Sopenharmony_ci pr_err("Missing A23 CPU config node in the device tree\n"); 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci cpucfg_membase = of_iomap(node, 0); 1528c2ecf20Sopenharmony_ci of_node_put(node); 1538c2ecf20Sopenharmony_ci if (!cpucfg_membase) 1548c2ecf20Sopenharmony_ci pr_err("Couldn't map A23 CPU config registers\n"); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int sun8i_smp_boot_secondary(unsigned int cpu, 1598c2ecf20Sopenharmony_ci struct task_struct *idle) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci u32 reg; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!(prcm_membase && cpucfg_membase)) 1648c2ecf20Sopenharmony_ci return -EFAULT; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci spin_lock(&cpu_lock); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Set CPU boot address */ 1698c2ecf20Sopenharmony_ci writel(__pa_symbol(secondary_startup), 1708c2ecf20Sopenharmony_ci cpucfg_membase + CPUCFG_PRIVATE0_REG); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Assert the CPU core in reset */ 1738c2ecf20Sopenharmony_ci writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu)); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Assert the L1 cache in reset */ 1768c2ecf20Sopenharmony_ci reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG); 1778c2ecf20Sopenharmony_ci writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Clear CPU power-off gating */ 1808c2ecf20Sopenharmony_ci reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG); 1818c2ecf20Sopenharmony_ci writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG); 1828c2ecf20Sopenharmony_ci mdelay(1); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Deassert the CPU core reset */ 1858c2ecf20Sopenharmony_ci writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu)); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci spin_unlock(&cpu_lock); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const struct smp_operations sun8i_smp_ops __initconst = { 1938c2ecf20Sopenharmony_ci .smp_prepare_cpus = sun8i_smp_prepare_cpus, 1948c2ecf20Sopenharmony_ci .smp_boot_secondary = sun8i_smp_boot_secondary, 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(sun8i_a23_smp, "allwinner,sun8i-a23", &sun8i_smp_ops); 197