xref: /netbsd-src/sys/arch/arm/sunxi/sunxi_mc_smp.c (revision 9cae9137acfdd7ffc6712f8130a72719a72025d6)
1 /* $NetBSD: sunxi_mc_smp.c,v 1.4 2019/03/03 17:00:22 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.4 2019/03/03 17:00:22 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 
86 extern struct bus_space arm_generic_bs_tag;
87 
88 enum sunxi_mc_soc {
89 	MC_SOC_A80,
90 	MC_SOC_A83T
91 };
92 
93 enum sunxi_mc_cpu {
94 	MC_CORE_CA7,
95 	MC_CORE_CA15
96 };
97 
98 uint32_t sunxi_mc_cci_port[MAXCPUS];
99 
100 static uint32_t
sunxi_mc_smp_pa(void)101 sunxi_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 
113 static int
sunxi_mc_smp_start(bus_space_tag_t bst,bus_space_handle_t prcm,bus_space_handle_t cpucfg,bus_space_handle_t cpuxcfg,u_int cluster,u_int cpu,enum sunxi_mc_soc soc,enum sunxi_mc_cpu core)114 sunxi_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 		else
160 			val &= ~__BIT(cpu);
161 		val &= ~__BIT(0);	/* cluster power gate */
162 	} else {
163 		val &= ~__BIT(cpu);
164 		val &= ~__BIT(4);	/* cluster power gate */
165 	}
166 	bus_space_write_4(bst, prcm, PRCM_CL_PWROFF(cluster), val);
167 
168 	/* De-assert power-on reset */
169 	val = bus_space_read_4(bst, prcm, PRCM_CL_RST_CTRL(cluster));
170 	val |= __BIT(cpu);
171 	bus_space_write_4(bst, prcm, PRCM_CL_RST_CTRL(cluster), val);
172 
173 	if (soc == MC_SOC_A83T) {
174 		val = bus_space_read_4(bst, cpucfg, CPUCFG_CL_RST(cluster));
175 		val |= __BIT(cpu);
176 		bus_space_write_4(bst, cpucfg, CPUCFG_CL_RST(cluster), val);
177 		delay(10);
178 	}
179 
180 	/* De-assert core reset */
181 	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster));
182 	val |= __BIT(cpu);
183 	val |= CPUXCFG_CL_RST_SOC_DBG_RST;
184 	if (core == MC_CORE_CA7)
185 		val |= CPUXCFG_CL_RST_ETM_RST(cpu);
186 	else
187 		val |= CPUXCFG_CL_RST_CX_RST(cpu);
188 	val |= CPUXCFG_CL_RST_DBG_RST(cpu);
189 	val |= CPUXCFG_CL_RST_L2_RST;
190 	val |= CPUXCFG_CL_RST_H_RST;
191 	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_RST(cluster), val);
192 
193 	/* De-assert ACINACTM */
194 	val = bus_space_read_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster));
195 	val &= ~CPUXCFG_CL_CTRL1_ACINACTM;
196 	bus_space_write_4(bst, cpuxcfg, CPUXCFG_CL_CTRL1(cluster), val);
197 
198 	return 0;
199 }
200 
201 int
sun8i_a83t_smp_enable(u_int mpidr)202 sun8i_a83t_smp_enable(u_int mpidr)
203 {
204 	bus_space_tag_t bst = &arm_generic_bs_tag;
205 	bus_space_handle_t prcm, cpucfg, cpuxcfg;
206 	int error;
207 
208 	const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1);
209 	const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0);
210 
211 	if (bus_space_map(bst, A83T_PRCM_BASE, A83T_PRCM_SIZE, 0, &prcm) != 0 ||
212 	    bus_space_map(bst, CPUCFG_BASE, CPUCFG_SIZE, 0, &cpucfg) != 0 ||
213 	    bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0)
214 		return ENOMEM;
215 
216 	for (int i = 0; i < 4; i++)
217 		sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF3_OFFSET;
218 	for (int i = 4; i < 8; i++)
219 		sunxi_mc_cci_port[i] = A83T_CCI_BASE + CCI_SLAVEIF4_OFFSET;
220 
221 	/* Set start vector */
222 	bus_space_write_4(bst, cpucfg, CPUCFG_P_REG0, sunxi_mc_smp_pa());
223 	cpu_idcache_wbinv_all();
224 
225 	error = sunxi_mc_smp_start(bst, prcm, cpucfg, cpuxcfg, cluster, cpu,
226 	    MC_SOC_A83T, MC_CORE_CA7);
227 
228 	bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE);
229 	bus_space_unmap(bst, cpucfg, CPUCFG_SIZE);
230 	bus_space_unmap(bst, prcm, A83T_PRCM_SIZE);
231 
232 	return error;
233 }
234 
235 int
sun9i_a80_smp_enable(u_int mpidr)236 sun9i_a80_smp_enable(u_int mpidr)
237 {
238 	bus_space_tag_t bst = &arm_generic_bs_tag;
239 	bus_space_handle_t prcm, cpuxcfg;
240 	int error;
241 
242 	const u_int cluster = __SHIFTOUT(mpidr, MPIDR_AFF1);
243 	const u_int cpu = __SHIFTOUT(mpidr, MPIDR_AFF0);
244 
245 	if (bus_space_map(bst, A80_PRCM_BASE, A80_PRCM_SIZE, 0, &prcm) != 0 ||
246 	    bus_space_map(bst, CPUXCFG_BASE, CPUXCFG_SIZE, 0, &cpuxcfg) != 0)
247 		return ENOMEM;
248 
249 	for (int i = 0; i < 4; i++)
250 		sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF3_OFFSET;
251 	for (int i = 4; i < 8; i++)
252 		sunxi_mc_cci_port[i] = A80_CCI_BASE + CCI_SLAVEIF4_OFFSET;
253 
254 	/* Set start vector */
255 	bus_space_write_4(bst, prcm, PRCM_CPU_SOFT_ENTRY, sunxi_mc_smp_pa());
256 	cpu_idcache_wbinv_all();
257 
258 	error = sunxi_mc_smp_start(bst, prcm, 0, cpuxcfg, cluster, cpu,
259 	    MC_SOC_A80, cluster == 0 ? MC_CORE_CA7 : MC_CORE_CA15);
260 
261 	bus_space_unmap(bst, cpuxcfg, CPUXCFG_SIZE);
262 	bus_space_unmap(bst, prcm, A80_PRCM_SIZE);
263 
264 	return error;
265 }
266