sunxi_mc_smp.c revision 1.3
1/* $NetBSD: sunxi_mc_smp.c,v 1.3 2019/01/03 14:44:21 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30
31__KERNEL_RCSID(0, "$NetBSD: sunxi_mc_smp.c,v 1.3 2019/01/03 14:44:21 jmcneill Exp $");
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/device.h>
36#include <sys/systm.h>
37
38#include <uvm/uvm_extern.h>
39
40#include <dev/fdt/fdtvar.h>
41
42#include <arm/armreg.h>
43#include <arm/cpu.h>
44#include <arm/cpufunc.h>
45#include <arm/locore.h>
46
47#include <arm/sunxi/sunxi_mc_smp.h>
48
49#define	A80_PRCM_BASE		0x08001400
50#define	A80_PRCM_SIZE		0x200
51
52#define	A83T_PRCM_BASE		0x01f01400
53#define	A83T_PRCM_SIZE		0x800
54
55#define	 PRCM_CL_RST_CTRL(cluster)	(0x4 + (cluster) * 0x4)
56#define	 PRCM_CL_PWROFF(cluster)	(0x100 + (cluster) * 0x4)
57#define	 PRCM_CL_PWR_CLAMP(cluster, cpu) (0x140 + (cluster) * 0x10 + (cpu) * 0x4)
58#define	 PRCM_CPU_SOFT_ENTRY		0x164
59
60#define	CPUCFG_BASE	0x01f01c00
61#define	CPUCFG_SIZE	0x400
62
63#define	 CPUCFG_CL_RST(cluster)		(0x30 + (cluster) * 0x4)
64#define	 CPUCFG_P_REG0			0x1a4
65
66#define	CPUXCFG_BASE	0x01700000
67#define	CPUXCFG_SIZE	0x400
68
69#define	 CPUXCFG_CL_RST(cluster)	(0x80 + (cluster) * 0x4)
70#define	  CPUXCFG_CL_RST_SOC_DBG_RST	__BIT(24)
71#define	  CPUXCFG_CL_RST_ETM_RST(cpu)	__BIT(20 + (cpu))
72#define	  CPUXCFG_CL_RST_DBG_RST(cpu)	__BIT(16 + (cpu))
73#define	  CPUXCFG_CL_RST_H_RST		__BIT(12)
74#define	  CPUXCFG_CL_RST_L2_RST		__BIT(8)
75#define	  CPUXCFG_CL_RST_CX_RST(cpu)	__BIT(4 + (cpu))
76#define	 CPUXCFG_CL_CTRL0(cluster)	(0x0 + (cluster) * 0x10)
77#define	 CPUXCFG_CL_CTRL1(cluster)	(0x4 + (cluster) * 0x10)
78#define	  CPUXCFG_CL_CTRL1_ACINACTM	__BIT(0)
79
80#define	A80_CCI_BASE		0x01c90000
81#define	A83T_CCI_BASE		0x01790000
82
83#define	CCI_SLAVEIF3_OFFSET	0x4000
84#define	CCI_SLAVEIF4_OFFSET	0x5000
85
86extern struct bus_space arm_generic_bs_tag;
87
88enum sunxi_mc_soc {
89	MC_SOC_A80,
90	MC_SOC_A83T
91};
92
93enum sunxi_mc_cpu {
94	MC_CORE_CA7,
95	MC_CORE_CA15
96};
97
98uint32_t sunxi_mc_cci_port[MAXCPUS];
99
100static uint32_t
101sunxi_mc_smp_pa(void)
102{
103	extern void sunxi_mc_mpstart(void);
104	bool ok __diagused;
105	paddr_t pa;
106
107	ok = pmap_extract(pmap_kernel(), (vaddr_t)sunxi_mc_mpstart, &pa);
108	KASSERT(ok);
109
110	return pa;
111}
112
113static int
114sunxi_mc_smp_start(bus_space_tag_t bst, bus_space_handle_t prcm, bus_space_handle_t cpucfg,
115    bus_space_handle_t cpuxcfg, u_int cluster, u_int cpu, enum sunxi_mc_soc soc,
116    enum sunxi_mc_cpu core)
117{
118	uint32_t val;
119	int i;
120
121	/* Assert core reset */
122	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster));
123	val &= ~__BIT(cpu);
124	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val);
125
126	if (soc == MC_SOC_A83T) {
127		/* Assert power-on reset */
128		val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster));
129		val &= ~__BIT(cpu);
130		bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val);
131	}
132
133	if (core == MC_CORE_CA7) {
134		/* Disable automatic L1 cache invalidate at reset */
135		val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster));
136		val &= ~__BIT(cpu);
137		bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL0(cluster), val);
138	}
139
140	/* Release power clamp */
141	for (i = 0; i <= 8; i++) {
142		bus_space_write_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i);
143		delay(10);
144	}
145	for (i = 100000; i > 0; i--) {
146		if (bus_space_read_4(bst, prcm, PRCM_CL_PWR_CLAMP(cluster, cpu)) == 0)
147			break;
148	}
149	if (i == 0) {
150		printf("CPU %#llx failed to start\n", __SHIFTIN(cluster, MPIDR_AFF1) | __SHIFTIN(cpu, MPIDR_AFF0));
151		return ETIMEDOUT;
152	}
153
154	/* Clear power-off gating */
155	val = bus_space_read_4(bst, prcm, PRCM_CL_PWROFF(cluster));
156	if (soc == MC_SOC_A83T) {
157		if (cpu == 0)
158			val &= ~__BIT(4);
159		val &= ~__BIT(0);	/* cluster power gate */
160	} else {
161		val &= ~__BIT(cpu);
162		val &= ~__BIT(4);	/* cluster power gate */
163	}
164	bus_space_write_4(bst, prcm, PRCM_CL_PWROFF(cluster), val);
165
166	/* De-assert power-on reset */
167	val = bus_space_read_4(bst, prcm, PRCM_CL_RST_CTRL(cluster));
168	val |= __BIT(cpu);
169	bus_space_write_4(bst, prcm, PRCM_CL_RST_CTRL(cluster), val);
170
171	if (soc == MC_SOC_A83T) {
172		val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster));
173		val |= __BIT(cpu);
174		bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val);
175		delay(10);
176	}
177
178	/* De-assert core reset */
179	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster));
180	val |= __BIT(cpu);
181	val |= CPUXCFG_CL_RST_SOC_DBG_RST;
182	if (core == MC_CORE_CA7)
183		val |= CPUXCFG_CL_RST_ETM_RST(cpu);
184	else
185		val |= CPUXCFG_CL_RST_CX_RST(cpu);
186	val |= CPUXCFG_CL_RST_DBG_RST(cpu);
187	val |= CPUXCFG_CL_RST_L2_RST;
188	val |= CPUXCFG_CL_RST_H_RST;
189	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val);
190
191	/* De-assert ACINACTM */
192	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster));
193	val &= ~CPUXCFG_CL_CTRL1_ACINACTM;
194	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster), val);
195
196	return 0;
197}
198
199int
200sun8i_a83t_smp_enable(u_int mpidr)
201{
202	bus_space_tag_t bst = &arm_generic_bs_tag;
203	bus_space_handle_t prcm, cpucfg, cpuxcfg;
204	int error;
205
206	const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1);
207	const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0);
208
209	if (bus_space_map(bst, A83T_PRCM_BASE, A83T_PRCM_SIZE, 0, &prcm) != 0 ||
210	    bus_space_map(bst, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0 ||
211	    bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0)
212		return ENOMEM;
213
214	for (int i = 0; i < 4; i++)
215		sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF3_OFFSET;
216	for (int i = 4; i < 8; i++)
217		sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF4_OFFSET;
218
219	/* Set start vector */
220	bus_space_write_4(bst, cpucfg, CPUCFG_P_REG0, sunxi_mc_smp_pa());
221	cpu_idcache_wbinv_all();
222
223	error = sunxi_mc_smp_start(bst, prcm, cpucfg, cpuxcfg, cluster, cpu,
224	    MC_SOC_A83T, MC_CORE_CA7);
225
226	bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE);
227	bus_space_unmap(bst, cpucfg, CPUCFG_SIZE);
228	bus_space_unmap(bst, prcm, A83T_PRCM_SIZE);
229
230	return error;
231}
232
233int
234sun9i_a80_smp_enable(u_int mpidr)
235{
236	bus_space_tag_t bst = &arm_generic_bs_tag;
237	bus_space_handle_t prcm, cpuxcfg;
238	int error;
239
240	const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1);
241	const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0);
242
243	if (bus_space_map(bst, A80_PRCM_BASE, A80_PRCM_SIZE, 0, &prcm) != 0 ||
244	    bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0)
245		return ENOMEM;
246
247	for (int i = 0; i < 4; i++)
248		sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF3_OFFSET;
249	for (int i = 4; i < 8; i++)
250		sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF4_OFFSET;
251
252	/* Set start vector */
253	bus_space_write_4(bst, prcm, PRCM_CPU_SOFT_ENTRY, sunxi_mc_smp_pa());
254	cpu_idcache_wbinv_all();
255
256	error = sunxi_mc_smp_start(bst, prcm, 0, cpuxcfg, cluster, cpu,
257	    MC_SOC_A80, cluster == 0 ? MC_CORE_CA7 : MC_CORE_CA15);
258
259	bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE);
260	bus_space_unmap(bst, prcm, A80_PRCM_SIZE);
261
262	return error;
263}
264