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