1*5923ddaaSJason Wang // SPDX-License-Identifier: GPL-2.0
273346794SMaxime Ripard /*
373346794SMaxime Ripard * SMP support for Allwinner SoCs
473346794SMaxime Ripard *
573346794SMaxime Ripard * Copyright (C) 2013 Maxime Ripard
673346794SMaxime Ripard *
773346794SMaxime Ripard * Maxime Ripard <maxime.ripard@free-electrons.com>
873346794SMaxime Ripard *
973346794SMaxime Ripard * Based on code
1073346794SMaxime Ripard * Copyright (C) 2012-2013 Allwinner Ltd.
1173346794SMaxime Ripard *
1273346794SMaxime Ripard */
1373346794SMaxime Ripard
1473346794SMaxime Ripard #include <linux/delay.h>
1573346794SMaxime Ripard #include <linux/init.h>
1673346794SMaxime Ripard #include <linux/io.h>
1773346794SMaxime Ripard #include <linux/memory.h>
1873346794SMaxime Ripard #include <linux/of.h>
1973346794SMaxime Ripard #include <linux/of_address.h>
2073346794SMaxime Ripard #include <linux/smp.h>
2173346794SMaxime Ripard
2273346794SMaxime Ripard #define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu) ((cpu) * 0x40 + 0x64)
2373346794SMaxime Ripard #define CPUCFG_CPU_RST_CTRL_REG(cpu) (((cpu) + 1) * 0x40)
2473346794SMaxime Ripard #define CPUCFG_CPU_CTRL_REG(cpu) (((cpu) + 1) * 0x40 + 0x04)
2573346794SMaxime Ripard #define CPUCFG_CPU_STATUS_REG(cpu) (((cpu) + 1) * 0x40 + 0x08)
2673346794SMaxime Ripard #define CPUCFG_GEN_CTRL_REG 0x184
2773346794SMaxime Ripard #define CPUCFG_PRIVATE0_REG 0x1a4
2873346794SMaxime Ripard #define CPUCFG_PRIVATE1_REG 0x1a8
2973346794SMaxime Ripard #define CPUCFG_DBG_CTL0_REG 0x1e0
3073346794SMaxime Ripard #define CPUCFG_DBG_CTL1_REG 0x1e4
3173346794SMaxime Ripard
3273346794SMaxime Ripard #define PRCM_CPU_PWROFF_REG 0x100
3373346794SMaxime Ripard #define PRCM_CPU_PWR_CLAMP_REG(cpu) (((cpu) * 4) + 0x140)
3473346794SMaxime Ripard
3573346794SMaxime Ripard static void __iomem *cpucfg_membase;
3673346794SMaxime Ripard static void __iomem *prcm_membase;
3773346794SMaxime Ripard
3873346794SMaxime Ripard static DEFINE_SPINLOCK(cpu_lock);
3973346794SMaxime Ripard
sun6i_smp_prepare_cpus(unsigned int max_cpus)4073346794SMaxime Ripard static void __init sun6i_smp_prepare_cpus(unsigned int max_cpus)
4173346794SMaxime Ripard {
4273346794SMaxime Ripard struct device_node *node;
4373346794SMaxime Ripard
4473346794SMaxime Ripard node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm");
4573346794SMaxime Ripard if (!node) {
4673346794SMaxime Ripard pr_err("Missing A31 PRCM node in the device tree\n");
4773346794SMaxime Ripard return;
4873346794SMaxime Ripard }
4973346794SMaxime Ripard
5073346794SMaxime Ripard prcm_membase = of_iomap(node, 0);
512b062a0aSWen Yang of_node_put(node);
5273346794SMaxime Ripard if (!prcm_membase) {
5373346794SMaxime Ripard pr_err("Couldn't map A31 PRCM registers\n");
5473346794SMaxime Ripard return;
5573346794SMaxime Ripard }
5673346794SMaxime Ripard
5773346794SMaxime Ripard node = of_find_compatible_node(NULL, NULL,
5873346794SMaxime Ripard "allwinner,sun6i-a31-cpuconfig");
5973346794SMaxime Ripard if (!node) {
6073346794SMaxime Ripard pr_err("Missing A31 CPU config node in the device tree\n");
6173346794SMaxime Ripard return;
6273346794SMaxime Ripard }
6373346794SMaxime Ripard
6473346794SMaxime Ripard cpucfg_membase = of_iomap(node, 0);
652b062a0aSWen Yang of_node_put(node);
6673346794SMaxime Ripard if (!cpucfg_membase)
6773346794SMaxime Ripard pr_err("Couldn't map A31 CPU config registers\n");
6873346794SMaxime Ripard
6973346794SMaxime Ripard }
7073346794SMaxime Ripard
sun6i_smp_boot_secondary(unsigned int cpu,struct task_struct * idle)7173346794SMaxime Ripard static int sun6i_smp_boot_secondary(unsigned int cpu,
7273346794SMaxime Ripard struct task_struct *idle)
7373346794SMaxime Ripard {
7473346794SMaxime Ripard u32 reg;
7573346794SMaxime Ripard int i;
7673346794SMaxime Ripard
7773346794SMaxime Ripard if (!(prcm_membase && cpucfg_membase))
7873346794SMaxime Ripard return -EFAULT;
7973346794SMaxime Ripard
8073346794SMaxime Ripard spin_lock(&cpu_lock);
8173346794SMaxime Ripard
8273346794SMaxime Ripard /* Set CPU boot address */
8364fc2a94SFlorian Fainelli writel(__pa_symbol(secondary_startup),
8473346794SMaxime Ripard cpucfg_membase + CPUCFG_PRIVATE0_REG);
8573346794SMaxime Ripard
8673346794SMaxime Ripard /* Assert the CPU core in reset */
8773346794SMaxime Ripard writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
8873346794SMaxime Ripard
8973346794SMaxime Ripard /* Assert the L1 cache in reset */
9073346794SMaxime Ripard reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
9173346794SMaxime Ripard writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
9273346794SMaxime Ripard
9373346794SMaxime Ripard /* Disable external debug access */
9473346794SMaxime Ripard reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
9573346794SMaxime Ripard writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
9673346794SMaxime Ripard
9773346794SMaxime Ripard /* Power up the CPU */
9873346794SMaxime Ripard for (i = 0; i <= 8; i++)
9973346794SMaxime Ripard writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu));
10073346794SMaxime Ripard mdelay(10);
10173346794SMaxime Ripard
10273346794SMaxime Ripard /* Clear CPU power-off gating */
10373346794SMaxime Ripard reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
10473346794SMaxime Ripard writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
10573346794SMaxime Ripard mdelay(1);
10673346794SMaxime Ripard
10773346794SMaxime Ripard /* Deassert the CPU core reset */
10873346794SMaxime Ripard writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
10973346794SMaxime Ripard
11073346794SMaxime Ripard /* Enable back the external debug accesses */
11173346794SMaxime Ripard reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
11273346794SMaxime Ripard writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
11373346794SMaxime Ripard
11473346794SMaxime Ripard spin_unlock(&cpu_lock);
11573346794SMaxime Ripard
11673346794SMaxime Ripard return 0;
11773346794SMaxime Ripard }
11873346794SMaxime Ripard
11975305275SMasahiro Yamada static const struct smp_operations sun6i_smp_ops __initconst = {
12073346794SMaxime Ripard .smp_prepare_cpus = sun6i_smp_prepare_cpus,
12173346794SMaxime Ripard .smp_boot_secondary = sun6i_smp_boot_secondary,
12273346794SMaxime Ripard };
123b0f2faa5SHans de Goede CPU_METHOD_OF_DECLARE(sun6i_a31_smp, "allwinner,sun6i-a31", &sun6i_smp_ops);
1247917d141SChen-Yu Tsai
sun8i_smp_prepare_cpus(unsigned int max_cpus)1257917d141SChen-Yu Tsai static void __init sun8i_smp_prepare_cpus(unsigned int max_cpus)
1267917d141SChen-Yu Tsai {
1277917d141SChen-Yu Tsai struct device_node *node;
1287917d141SChen-Yu Tsai
1297917d141SChen-Yu Tsai node = of_find_compatible_node(NULL, NULL, "allwinner,sun8i-a23-prcm");
1307917d141SChen-Yu Tsai if (!node) {
1317917d141SChen-Yu Tsai pr_err("Missing A23 PRCM node in the device tree\n");
1327917d141SChen-Yu Tsai return;
1337917d141SChen-Yu Tsai }
1347917d141SChen-Yu Tsai
1357917d141SChen-Yu Tsai prcm_membase = of_iomap(node, 0);
136995c770bSWen Yang of_node_put(node);
1377917d141SChen-Yu Tsai if (!prcm_membase) {
1387917d141SChen-Yu Tsai pr_err("Couldn't map A23 PRCM registers\n");
1397917d141SChen-Yu Tsai return;
1407917d141SChen-Yu Tsai }
1417917d141SChen-Yu Tsai
1427917d141SChen-Yu Tsai node = of_find_compatible_node(NULL, NULL,
1437917d141SChen-Yu Tsai "allwinner,sun8i-a23-cpuconfig");
1447917d141SChen-Yu Tsai if (!node) {
1457917d141SChen-Yu Tsai pr_err("Missing A23 CPU config node in the device tree\n");
1467917d141SChen-Yu Tsai return;
1477917d141SChen-Yu Tsai }
1487917d141SChen-Yu Tsai
1497917d141SChen-Yu Tsai cpucfg_membase = of_iomap(node, 0);
150995c770bSWen Yang of_node_put(node);
1517917d141SChen-Yu Tsai if (!cpucfg_membase)
1527917d141SChen-Yu Tsai pr_err("Couldn't map A23 CPU config registers\n");
1537917d141SChen-Yu Tsai
1547917d141SChen-Yu Tsai }
1557917d141SChen-Yu Tsai
sun8i_smp_boot_secondary(unsigned int cpu,struct task_struct * idle)1567917d141SChen-Yu Tsai static int sun8i_smp_boot_secondary(unsigned int cpu,
1577917d141SChen-Yu Tsai struct task_struct *idle)
1587917d141SChen-Yu Tsai {
1597917d141SChen-Yu Tsai u32 reg;
1607917d141SChen-Yu Tsai
1617917d141SChen-Yu Tsai if (!(prcm_membase && cpucfg_membase))
1627917d141SChen-Yu Tsai return -EFAULT;
1637917d141SChen-Yu Tsai
1647917d141SChen-Yu Tsai spin_lock(&cpu_lock);
1657917d141SChen-Yu Tsai
1667917d141SChen-Yu Tsai /* Set CPU boot address */
16764fc2a94SFlorian Fainelli writel(__pa_symbol(secondary_startup),
1687917d141SChen-Yu Tsai cpucfg_membase + CPUCFG_PRIVATE0_REG);
1697917d141SChen-Yu Tsai
1707917d141SChen-Yu Tsai /* Assert the CPU core in reset */
1717917d141SChen-Yu Tsai writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
1727917d141SChen-Yu Tsai
1737917d141SChen-Yu Tsai /* Assert the L1 cache in reset */
1747917d141SChen-Yu Tsai reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
1757917d141SChen-Yu Tsai writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
1767917d141SChen-Yu Tsai
1777917d141SChen-Yu Tsai /* Clear CPU power-off gating */
1787917d141SChen-Yu Tsai reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
1797917d141SChen-Yu Tsai writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
1807917d141SChen-Yu Tsai mdelay(1);
1817917d141SChen-Yu Tsai
1827917d141SChen-Yu Tsai /* Deassert the CPU core reset */
1837917d141SChen-Yu Tsai writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
1847917d141SChen-Yu Tsai
1857917d141SChen-Yu Tsai spin_unlock(&cpu_lock);
1867917d141SChen-Yu Tsai
1877917d141SChen-Yu Tsai return 0;
1887917d141SChen-Yu Tsai }
1897917d141SChen-Yu Tsai
19075305275SMasahiro Yamada static const struct smp_operations sun8i_smp_ops __initconst = {
1917917d141SChen-Yu Tsai .smp_prepare_cpus = sun8i_smp_prepare_cpus,
1927917d141SChen-Yu Tsai .smp_boot_secondary = sun8i_smp_boot_secondary,
1937917d141SChen-Yu Tsai };
1947917d141SChen-Yu Tsai CPU_METHOD_OF_DECLARE(sun8i_a23_smp, "allwinner,sun8i-a23", &sun8i_smp_ops);
195