xref: /netbsd-src/sys/arch/arm/fdt/cpu_fdt.c (revision 7b4ce397cdd4c2ca586d2a402422262e1e25cc87)
1 /* $NetBSD: cpu_fdt.c,v 1.44 2024/05/10 14:42:21 riastradh Exp $ */
2 
3 /*-
4  * Copyright (c) 2017 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 "opt_multiprocessor.h"
30 #include "psci_fdt.h"
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.44 2024/05/10 14:42:21 riastradh Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/atomic.h>
37 #include <sys/bus.h>
38 #include <sys/device.h>
39 #include <sys/lwp.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 
43 #include <dev/fdt/fdtvar.h>
44 
45 #include <arm/armreg.h>
46 #include <arm/cpu.h>
47 #include <arm/cpufunc.h>
48 #include <arm/cpuvar.h>
49 #include <arm/locore.h>
50 
51 #include <arm/arm/psci.h>
52 #include <arm/fdt/arm_fdtvar.h>
53 #include <arm/fdt/psci_fdtvar.h>
54 
55 #include <uvm/uvm_extern.h>
56 
57 static int	cpu_fdt_match(device_t, cfdata_t, void *);
58 static void	cpu_fdt_attach(device_t, device_t, void *);
59 
60 CFATTACH_DECL2_NEW(cpu_fdt, 0,
61     cpu_fdt_match, cpu_fdt_attach, NULL, NULL,
62     cpu_rescan, cpu_childdetached);
63 
64 static int
cpu_fdt_match(device_t parent,cfdata_t cf,void * aux)65 cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
66 {
67 	struct fdt_attach_args * const faa = aux;
68 	const int phandle = faa->faa_phandle;
69 	const char *device_type;
70 
71 	device_type = fdtbus_get_string(phandle, "device_type");
72 
73 	return device_type != NULL && strcmp(device_type, "cpu") == 0;
74 }
75 
76 static void
cpu_fdt_attach(device_t parent,device_t self,void * aux)77 cpu_fdt_attach(device_t parent, device_t self, void *aux)
78 {
79 	struct fdt_attach_args * const faa = aux;
80 	const int phandle = faa->faa_phandle;
81 	bus_addr_t cpuid;
82 	const uint32_t *cap_ptr;
83 	int len;
84 
85  	cap_ptr = fdtbus_get_prop(phandle, "capacity-dmips-mhz", &len);
86 	if (cap_ptr && len == 4) {
87 		prop_dictionary_t dict = device_properties(self);
88 		uint32_t capacity_dmips_mhz = be32toh(*cap_ptr);
89 
90 		prop_dictionary_set_uint32(dict, "capacity_dmips_mhz",
91 		    capacity_dmips_mhz);
92 	}
93 
94 	if (fdtbus_get_reg(phandle, 0, &cpuid, NULL) != 0)
95 		cpuid = 0;
96 
97 	/* Attach the CPU */
98 	cpu_attach(self, cpuid);
99 
100 	/* Attach CPU frequency scaling provider */
101 	config_found(self, faa, NULL, CFARGS(.iattr = "cpu"));
102 }
103 
104 #if defined(MULTIPROCESSOR) && (NPSCI_FDT > 0 || defined(__aarch64__))
105 static register_t
cpu_fdt_mpstart_pa(void)106 cpu_fdt_mpstart_pa(void)
107 {
108 	bool ok __diagused;
109 	paddr_t pa;
110 
111 	ok = pmap_extract(pmap_kernel(), (vaddr_t)cpu_mpstart, &pa);
112 	KASSERT(ok);
113 
114 	return pa;
115 }
116 #endif
117 
118 #ifdef MULTIPROCESSOR
119 static bool
arm_fdt_cpu_okay(const int child)120 arm_fdt_cpu_okay(const int child)
121 {
122 	const char *s;
123 
124 	s = fdtbus_get_string(child, "device_type");
125 	if (!s || strcmp(s, "cpu") != 0)
126 		return false;
127 
128 	s = fdtbus_get_string(child, "status");
129 	if (s) {
130 		if (strcmp(s, "okay") == 0)
131 			return false;
132 		if (strcmp(s, "disabled") == 0)
133 			return of_hasprop(child, "enable-method");
134 		return false;
135 	} else {
136 		return true;
137 	}
138 }
139 #endif /* MULTIPROCESSOR */
140 
141 void
arm_fdt_cpu_bootstrap(void)142 arm_fdt_cpu_bootstrap(void)
143 {
144 #ifdef MULTIPROCESSOR
145 	uint64_t mpidr, bp_mpidr;
146 	u_int cpuindex;
147 	int child;
148 
149 	const int cpus = OF_finddevice("/cpus");
150 	if (cpus == -1) {
151 		aprint_error("%s: no /cpus node found\n", __func__);
152 		arm_cpu_max = 1;
153 		return;
154 	}
155 
156 	/* Count CPUs */
157 	arm_cpu_max = 0;
158 
159 	/* MPIDR affinity levels of boot processor. */
160 	bp_mpidr = cpu_mpidr_aff_read();
161 
162 	/* Add APs to cpu_mpidr array */
163 	cpuindex = 1;
164 	for (child = OF_child(cpus); child; child = OF_peer(child)) {
165 		if (!arm_fdt_cpu_okay(child))
166 			continue;
167 
168 		arm_cpu_max++;
169 		if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
170 			continue;
171 		if (mpidr == bp_mpidr)
172 			continue; 	/* BP already started */
173 
174 		KASSERT(cpuindex < MAXCPUS);
175 		cpu_mpidr[cpuindex] = mpidr;
176 		cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex],
177 		    sizeof(cpu_mpidr[cpuindex]));
178 
179 		cpuindex++;
180 	}
181 #endif
182 }
183 
184 #ifdef MULTIPROCESSOR
185 static struct arm_cpu_method *
arm_fdt_cpu_enable_method_byname(const char * method)186 arm_fdt_cpu_enable_method_byname(const char *method)
187 {
188 	__link_set_decl(arm_cpu_methods, struct arm_cpu_method);
189 	struct arm_cpu_method * const *acmp;
190 
191 	__link_set_foreach(acmp, arm_cpu_methods) {
192 		if (strcmp(method, (*acmp)->acm_compat) == 0)
193 			return *acmp;
194 	}
195 
196 	return NULL;
197 }
198 
199 static struct arm_cpu_method *
arm_fdt_cpu_enable_method(int phandle)200 arm_fdt_cpu_enable_method(int phandle)
201 {
202 	const char *method;
203 
204  	method = fdtbus_get_string(phandle, "enable-method");
205 	if (method == NULL)
206 		return NULL;
207 
208 	return arm_fdt_cpu_enable_method_byname(method);
209 }
210 
211 static int
arm_fdt_cpu_enable(int phandle,struct arm_cpu_method * acm)212 arm_fdt_cpu_enable(int phandle, struct arm_cpu_method *acm)
213 {
214 	return acm->acm_enable(phandle);
215 }
216 #endif
217 
218 int
arm_fdt_cpu_mpstart(void)219 arm_fdt_cpu_mpstart(void)
220 {
221 	int ret = 0;
222 #ifdef MULTIPROCESSOR
223 	uint64_t mpidr, bp_mpidr;
224 	u_int cpuindex, i;
225 	int child, error;
226 	struct arm_cpu_method *acm;
227 
228 	const int cpus = OF_finddevice("/cpus");
229 	if (cpus == -1) {
230 		aprint_error("%s: no /cpus node found\n", __func__);
231 		return 0;
232 	}
233 
234 	/* MPIDR affinity levels of boot processor. */
235 	bp_mpidr = cpu_mpidr_aff_read();
236 
237 	/* Boot APs */
238 	cpuindex = 1;
239 	for (child = OF_child(cpus); child; child = OF_peer(child)) {
240 		if (!arm_fdt_cpu_okay(child))
241 			continue;
242 
243 		if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
244 			continue;
245 
246 		if (mpidr == bp_mpidr)
247 			continue; 	/* BP already started */
248 
249 		acm = arm_fdt_cpu_enable_method(child);
250 		if (acm == NULL)
251 			acm = arm_fdt_cpu_enable_method(cpus);
252 		if (acm == NULL)
253 			acm = arm_fdt_cpu_enable_method_byname("psci");
254 		if (acm == NULL)
255 			continue;
256 
257 		error = arm_fdt_cpu_enable(child, acm);
258 		if (error != 0) {
259 			aprint_error("%s: failed to enable CPU %#" PRIx64 "\n",
260 			    __func__, mpidr);
261 			continue;
262 		}
263 
264 		/* Wake up AP in case firmware has placed it in WFE state */
265 		sev();
266 
267 		/* Wait for AP to start */
268 		for (i = 0x10000000; i > 0; i--) {
269 			if (cpu_hatched_p(cpuindex))
270 				break;
271 		}
272 
273 		if (i == 0) {
274 			ret++;
275 			aprint_error("cpu%d: WARNING: AP failed to start\n", cpuindex);
276 		}
277 
278 		cpuindex++;
279 	}
280 #endif /* MULTIPROCESSOR */
281 	return ret;
282 }
283 
284 static int
cpu_enable_nullop(int phandle)285 cpu_enable_nullop(int phandle)
286 {
287 	return ENXIO;
288 }
289 ARM_CPU_METHOD(default, "", cpu_enable_nullop);
290 
291 #if defined(MULTIPROCESSOR) && NPSCI_FDT > 0
292 static int
cpu_enable_psci(int phandle)293 cpu_enable_psci(int phandle)
294 {
295 	static bool psci_probed, psci_p;
296 	uint64_t mpidr;
297 	int ret;
298 
299 	if (!psci_probed) {
300 		psci_probed = true;
301 		psci_p = psci_fdt_preinit() == 0;
302 	}
303 	if (!psci_p)
304 		return ENXIO;
305 
306 	fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
307 
308 #if !defined(AARCH64)
309 	/*
310 	 * not necessary on AARCH64. beside there it hangs the system
311 	 * because cache ops are only functional after cpu_attach()
312 	 * was called.
313 	 */
314 	cpu_dcache_wbinv_all();
315 #endif
316 	ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0);
317 	if (ret != PSCI_SUCCESS)
318 		return EIO;
319 
320 	return 0;
321 }
322 ARM_CPU_METHOD(psci, "psci", cpu_enable_psci);
323 #endif
324 
325 #if defined(MULTIPROCESSOR) && defined(__aarch64__)
326 static int
spintable_cpu_on(const int phandle,u_int cpuindex,paddr_t entry_point_address,paddr_t cpu_release_addr)327 spintable_cpu_on(const int phandle, u_int cpuindex,
328     paddr_t entry_point_address, paddr_t cpu_release_addr)
329 {
330 	/*
331 	 * we need devmap for cpu-release-addr in advance.
332 	 * __HAVE_MM_MD_DIRECT_MAPPED_PHYS nor pmap work at this point.
333 	 */
334 	if (pmap_devmap_find_pa(cpu_release_addr, sizeof(paddr_t)) == NULL) {
335 		aprint_error("%s: devmap for cpu-release-addr"
336 		    " 0x%08"PRIxPADDR" required\n", __func__, cpu_release_addr);
337 		return -1;
338 	} else {
339 		extern struct bus_space arm_generic_bs_tag;
340 		bus_space_handle_t ioh;
341 
342 		const int parent = OF_parent(phandle);
343 		const int addr_cells = fdtbus_get_addr_cells(parent);
344 
345 		bus_space_map(&arm_generic_bs_tag, cpu_release_addr,
346 		    sizeof(paddr_t), 0, &ioh);
347 		if (addr_cells == 1) {
348 			bus_space_write_4(&arm_generic_bs_tag, ioh, 0,
349 			    entry_point_address);
350 		} else {
351 			bus_space_write_8(&arm_generic_bs_tag, ioh, 0,
352 			    entry_point_address);
353 		}
354 		bus_space_unmap(&arm_generic_bs_tag, ioh, sizeof(paddr_t));
355 	}
356 
357 	return 0;
358 }
359 
360 static int
cpu_enable_spin_table(int phandle)361 cpu_enable_spin_table(int phandle)
362 {
363 	uint64_t mpidr, addr;
364 	int ret;
365 
366 	fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
367 
368 	if (of_getprop_uint64(phandle, "cpu-release-addr", &addr) != 0)
369 		return ENXIO;
370 
371 	ret = spintable_cpu_on(phandle, mpidr, cpu_fdt_mpstart_pa(),
372 	    (paddr_t)addr);
373 	if (ret != 0)
374 		return EIO;
375 
376 	return 0;
377 }
378 ARM_CPU_METHOD(spin_table, "spin-table", cpu_enable_spin_table);
379 #endif
380