162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SMP support for Allwinner SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Maxime Ripard
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Based on code
1062306a36Sopenharmony_ci *  Copyright (C) 2012-2013 Allwinner Ltd.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/memory.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/of_address.h>
2062306a36Sopenharmony_ci#include <linux/smp.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu)	((cpu) * 0x40 + 0x64)
2362306a36Sopenharmony_ci#define CPUCFG_CPU_RST_CTRL_REG(cpu)		(((cpu) + 1) * 0x40)
2462306a36Sopenharmony_ci#define CPUCFG_CPU_CTRL_REG(cpu)		(((cpu) + 1) * 0x40 + 0x04)
2562306a36Sopenharmony_ci#define CPUCFG_CPU_STATUS_REG(cpu)		(((cpu) + 1) * 0x40 + 0x08)
2662306a36Sopenharmony_ci#define CPUCFG_GEN_CTRL_REG			0x184
2762306a36Sopenharmony_ci#define CPUCFG_PRIVATE0_REG			0x1a4
2862306a36Sopenharmony_ci#define CPUCFG_PRIVATE1_REG			0x1a8
2962306a36Sopenharmony_ci#define CPUCFG_DBG_CTL0_REG			0x1e0
3062306a36Sopenharmony_ci#define CPUCFG_DBG_CTL1_REG			0x1e4
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define PRCM_CPU_PWROFF_REG			0x100
3362306a36Sopenharmony_ci#define PRCM_CPU_PWR_CLAMP_REG(cpu)		(((cpu) * 4) + 0x140)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void __iomem *cpucfg_membase;
3662306a36Sopenharmony_cistatic void __iomem *prcm_membase;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(cpu_lock);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void __init sun6i_smp_prepare_cpus(unsigned int max_cpus)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct device_node *node;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm");
4562306a36Sopenharmony_ci	if (!node) {
4662306a36Sopenharmony_ci		pr_err("Missing A31 PRCM node in the device tree\n");
4762306a36Sopenharmony_ci		return;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	prcm_membase = of_iomap(node, 0);
5162306a36Sopenharmony_ci	of_node_put(node);
5262306a36Sopenharmony_ci	if (!prcm_membase) {
5362306a36Sopenharmony_ci		pr_err("Couldn't map A31 PRCM registers\n");
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL,
5862306a36Sopenharmony_ci				       "allwinner,sun6i-a31-cpuconfig");
5962306a36Sopenharmony_ci	if (!node) {
6062306a36Sopenharmony_ci		pr_err("Missing A31 CPU config node in the device tree\n");
6162306a36Sopenharmony_ci		return;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	cpucfg_membase = of_iomap(node, 0);
6562306a36Sopenharmony_ci	of_node_put(node);
6662306a36Sopenharmony_ci	if (!cpucfg_membase)
6762306a36Sopenharmony_ci		pr_err("Couldn't map A31 CPU config registers\n");
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int sun6i_smp_boot_secondary(unsigned int cpu,
7262306a36Sopenharmony_ci				    struct task_struct *idle)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	u32 reg;
7562306a36Sopenharmony_ci	int i;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!(prcm_membase && cpucfg_membase))
7862306a36Sopenharmony_ci		return -EFAULT;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	spin_lock(&cpu_lock);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Set CPU boot address */
8362306a36Sopenharmony_ci	writel(__pa_symbol(secondary_startup),
8462306a36Sopenharmony_ci	       cpucfg_membase + CPUCFG_PRIVATE0_REG);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Assert the CPU core in reset */
8762306a36Sopenharmony_ci	writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Assert the L1 cache in reset */
9062306a36Sopenharmony_ci	reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
9162306a36Sopenharmony_ci	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Disable external debug access */
9462306a36Sopenharmony_ci	reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
9562306a36Sopenharmony_ci	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* Power up the CPU */
9862306a36Sopenharmony_ci	for (i = 0; i <= 8; i++)
9962306a36Sopenharmony_ci		writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu));
10062306a36Sopenharmony_ci	mdelay(10);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Clear CPU power-off gating */
10362306a36Sopenharmony_ci	reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
10462306a36Sopenharmony_ci	writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
10562306a36Sopenharmony_ci	mdelay(1);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Deassert the CPU core reset */
10862306a36Sopenharmony_ci	writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Enable back the external debug accesses */
11162306a36Sopenharmony_ci	reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
11262306a36Sopenharmony_ci	writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	spin_unlock(&cpu_lock);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic const struct smp_operations sun6i_smp_ops __initconst = {
12062306a36Sopenharmony_ci	.smp_prepare_cpus	= sun6i_smp_prepare_cpus,
12162306a36Sopenharmony_ci	.smp_boot_secondary	= sun6i_smp_boot_secondary,
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(sun6i_a31_smp, "allwinner,sun6i-a31", &sun6i_smp_ops);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void __init sun8i_smp_prepare_cpus(unsigned int max_cpus)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct device_node *node;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL, "allwinner,sun8i-a23-prcm");
13062306a36Sopenharmony_ci	if (!node) {
13162306a36Sopenharmony_ci		pr_err("Missing A23 PRCM node in the device tree\n");
13262306a36Sopenharmony_ci		return;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	prcm_membase = of_iomap(node, 0);
13662306a36Sopenharmony_ci	of_node_put(node);
13762306a36Sopenharmony_ci	if (!prcm_membase) {
13862306a36Sopenharmony_ci		pr_err("Couldn't map A23 PRCM registers\n");
13962306a36Sopenharmony_ci		return;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL,
14362306a36Sopenharmony_ci				       "allwinner,sun8i-a23-cpuconfig");
14462306a36Sopenharmony_ci	if (!node) {
14562306a36Sopenharmony_ci		pr_err("Missing A23 CPU config node in the device tree\n");
14662306a36Sopenharmony_ci		return;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	cpucfg_membase = of_iomap(node, 0);
15062306a36Sopenharmony_ci	of_node_put(node);
15162306a36Sopenharmony_ci	if (!cpucfg_membase)
15262306a36Sopenharmony_ci		pr_err("Couldn't map A23 CPU config registers\n");
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int sun8i_smp_boot_secondary(unsigned int cpu,
15762306a36Sopenharmony_ci				    struct task_struct *idle)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	u32 reg;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!(prcm_membase && cpucfg_membase))
16262306a36Sopenharmony_ci		return -EFAULT;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	spin_lock(&cpu_lock);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Set CPU boot address */
16762306a36Sopenharmony_ci	writel(__pa_symbol(secondary_startup),
16862306a36Sopenharmony_ci	       cpucfg_membase + CPUCFG_PRIVATE0_REG);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* Assert the CPU core in reset */
17162306a36Sopenharmony_ci	writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Assert the L1 cache in reset */
17462306a36Sopenharmony_ci	reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
17562306a36Sopenharmony_ci	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Clear CPU power-off gating */
17862306a36Sopenharmony_ci	reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
17962306a36Sopenharmony_ci	writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
18062306a36Sopenharmony_ci	mdelay(1);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Deassert the CPU core reset */
18362306a36Sopenharmony_ci	writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	spin_unlock(&cpu_lock);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic const struct smp_operations sun8i_smp_ops __initconst = {
19162306a36Sopenharmony_ci	.smp_prepare_cpus	= sun8i_smp_prepare_cpus,
19262306a36Sopenharmony_ci	.smp_boot_secondary	= sun8i_smp_boot_secondary,
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(sun8i_a23_smp, "allwinner,sun8i-a23", &sun8i_smp_ops);
195