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