1// SPDX-License-Identifier: GPL-2.0
2/*
3 * SMP support for Allwinner SoCs
4 *
5 * Copyright (C) 2013 Maxime Ripard
6 *
7 * Maxime Ripard <maxime.ripard@free-electrons.com>
8 *
9 * Based on code
10 *  Copyright (C) 2012-2013 Allwinner Ltd.
11 *
12 */
13
14#include <linux/delay.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/memory.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20#include <linux/smp.h>
21
22#define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu)	((cpu) * 0x40 + 0x64)
23#define CPUCFG_CPU_RST_CTRL_REG(cpu)		(((cpu) + 1) * 0x40)
24#define CPUCFG_CPU_CTRL_REG(cpu)		(((cpu) + 1) * 0x40 + 0x04)
25#define CPUCFG_CPU_STATUS_REG(cpu)		(((cpu) + 1) * 0x40 + 0x08)
26#define CPUCFG_GEN_CTRL_REG			0x184
27#define CPUCFG_PRIVATE0_REG			0x1a4
28#define CPUCFG_PRIVATE1_REG			0x1a8
29#define CPUCFG_DBG_CTL0_REG			0x1e0
30#define CPUCFG_DBG_CTL1_REG			0x1e4
31
32#define PRCM_CPU_PWROFF_REG			0x100
33#define PRCM_CPU_PWR_CLAMP_REG(cpu)		(((cpu) * 4) + 0x140)
34
35static void __iomem *cpucfg_membase;
36static void __iomem *prcm_membase;
37
38static DEFINE_SPINLOCK(cpu_lock);
39
40static void __init sun6i_smp_prepare_cpus(unsigned int max_cpus)
41{
42	struct device_node *node;
43
44	node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm");
45	if (!node) {
46		pr_err("Missing A31 PRCM node in the device tree\n");
47		return;
48	}
49
50	prcm_membase = of_iomap(node, 0);
51	of_node_put(node);
52	if (!prcm_membase) {
53		pr_err("Couldn't map A31 PRCM registers\n");
54		return;
55	}
56
57	node = of_find_compatible_node(NULL, NULL,
58				       "allwinner,sun6i-a31-cpuconfig");
59	if (!node) {
60		pr_err("Missing A31 CPU config node in the device tree\n");
61		return;
62	}
63
64	cpucfg_membase = of_iomap(node, 0);
65	of_node_put(node);
66	if (!cpucfg_membase)
67		pr_err("Couldn't map A31 CPU config registers\n");
68
69}
70
71static int sun6i_smp_boot_secondary(unsigned int cpu,
72				    struct task_struct *idle)
73{
74	u32 reg;
75	int i;
76
77	if (!(prcm_membase && cpucfg_membase))
78		return -EFAULT;
79
80	spin_lock(&cpu_lock);
81
82	/* Set CPU boot address */
83	writel(__pa_symbol(secondary_startup),
84	       cpucfg_membase + CPUCFG_PRIVATE0_REG);
85
86	/* Assert the CPU core in reset */
87	writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
88
89	/* Assert the L1 cache in reset */
90	reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
91	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
92
93	/* Disable external debug access */
94	reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
95	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
96
97	/* Power up the CPU */
98	for (i = 0; i <= 8; i++)
99		writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu));
100	mdelay(10);
101
102	/* Clear CPU power-off gating */
103	reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
104	writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
105	mdelay(1);
106
107	/* Deassert the CPU core reset */
108	writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
109
110	/* Enable back the external debug accesses */
111	reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
112	writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);
113
114	spin_unlock(&cpu_lock);
115
116	return 0;
117}
118
119static const struct smp_operations sun6i_smp_ops __initconst = {
120	.smp_prepare_cpus	= sun6i_smp_prepare_cpus,
121	.smp_boot_secondary	= sun6i_smp_boot_secondary,
122};
123CPU_METHOD_OF_DECLARE(sun6i_a31_smp, "allwinner,sun6i-a31", &sun6i_smp_ops);
124
125static void __init sun8i_smp_prepare_cpus(unsigned int max_cpus)
126{
127	struct device_node *node;
128
129	node = of_find_compatible_node(NULL, NULL, "allwinner,sun8i-a23-prcm");
130	if (!node) {
131		pr_err("Missing A23 PRCM node in the device tree\n");
132		return;
133	}
134
135	prcm_membase = of_iomap(node, 0);
136	of_node_put(node);
137	if (!prcm_membase) {
138		pr_err("Couldn't map A23 PRCM registers\n");
139		return;
140	}
141
142	node = of_find_compatible_node(NULL, NULL,
143				       "allwinner,sun8i-a23-cpuconfig");
144	if (!node) {
145		pr_err("Missing A23 CPU config node in the device tree\n");
146		return;
147	}
148
149	cpucfg_membase = of_iomap(node, 0);
150	of_node_put(node);
151	if (!cpucfg_membase)
152		pr_err("Couldn't map A23 CPU config registers\n");
153
154}
155
156static int sun8i_smp_boot_secondary(unsigned int cpu,
157				    struct task_struct *idle)
158{
159	u32 reg;
160
161	if (!(prcm_membase && cpucfg_membase))
162		return -EFAULT;
163
164	spin_lock(&cpu_lock);
165
166	/* Set CPU boot address */
167	writel(__pa_symbol(secondary_startup),
168	       cpucfg_membase + CPUCFG_PRIVATE0_REG);
169
170	/* Assert the CPU core in reset */
171	writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
172
173	/* Assert the L1 cache in reset */
174	reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
175	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);
176
177	/* Clear CPU power-off gating */
178	reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
179	writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
180	mdelay(1);
181
182	/* Deassert the CPU core reset */
183	writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));
184
185	spin_unlock(&cpu_lock);
186
187	return 0;
188}
189
190static const struct smp_operations sun8i_smp_ops __initconst = {
191	.smp_prepare_cpus	= sun8i_smp_prepare_cpus,
192	.smp_boot_secondary	= sun8i_smp_boot_secondary,
193};
194CPU_METHOD_OF_DECLARE(sun8i_a23_smp, "allwinner,sun8i-a23", &sun8i_smp_ops);
195