cpu_fdt.c revision 1.16 1 /* $NetBSD: cpu_fdt.c,v 1.16 2018/10/18 09:01:52 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2017 Jared McNeill <jmcneill (at) 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.16 2018/10/18 09:01:52 skrll 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/locore.h>
49
50 #include <arm/arm/psci.h>
51 #include <arm/fdt/arm_fdtvar.h>
52 #include <arm/fdt/psci_fdtvar.h>
53
54 #include <uvm/uvm_extern.h>
55
56 static int cpu_fdt_match(device_t, cfdata_t, void *);
57 static void cpu_fdt_attach(device_t, device_t, void *);
58
59 enum cpu_fdt_type {
60 ARM_CPU_UP = 1,
61 ARM_CPU_ARMV7,
62 ARM_CPU_ARMV8,
63 };
64
65 struct cpu_fdt_softc {
66 device_t sc_dev;
67 int sc_phandle;
68 };
69
70 static const struct of_compat_data compat_data[] = {
71 { "arm,arm1176jzf-s", ARM_CPU_UP },
72
73 { "arm,arm-v7", ARM_CPU_ARMV7 },
74 { "arm,cortex-a5", ARM_CPU_ARMV7 },
75 { "arm,cortex-a7", ARM_CPU_ARMV7 },
76 { "arm,cortex-a8", ARM_CPU_ARMV7 },
77 { "arm,cortex-a9", ARM_CPU_ARMV7 },
78 { "arm,cortex-a12", ARM_CPU_ARMV7 },
79 { "arm,cortex-a15", ARM_CPU_ARMV7 },
80 { "arm,cortex-a17", ARM_CPU_ARMV7 },
81
82 { "arm,armv8", ARM_CPU_ARMV8 },
83 { "arm,cortex-a53", ARM_CPU_ARMV8 },
84 { "arm,cortex-a57", ARM_CPU_ARMV8 },
85 { "arm,cortex-a72", ARM_CPU_ARMV8 },
86 { "arm,cortex-a73", ARM_CPU_ARMV8 },
87
88 { NULL }
89 };
90
91 CFATTACH_DECL_NEW(cpu_fdt, sizeof(struct cpu_fdt_softc),
92 cpu_fdt_match, cpu_fdt_attach, NULL, NULL);
93
94 static int
95 cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
96 {
97 struct fdt_attach_args * const faa = aux;
98 const int phandle = faa->faa_phandle;
99 enum cpu_fdt_type type;
100 int is_compatible;
101 bus_addr_t mpidr;
102
103 is_compatible = of_match_compat_data(phandle, compat_data);
104 if (!is_compatible)
105 return 0;
106
107 type = of_search_compatible(phandle, compat_data)->data;
108 switch (type) {
109 case ARM_CPU_ARMV7:
110 case ARM_CPU_ARMV8:
111 if (fdtbus_get_reg(phandle, 0, &mpidr, NULL) != 0)
112 return 0;
113
114 #ifndef __aarch64__
115 /* XXX NetBSD/arm requires all CPUs to be in the same cluster */
116 const u_int bp_clid = cpu_clusterid();
117 const u_int clid = __SHIFTOUT(mpidr, MPIDR_AFF1);
118
119 if (bp_clid != clid)
120 return 0;
121 #endif
122 break;
123 default:
124 break;
125 }
126
127 return is_compatible;
128 }
129
130 static void
131 cpu_fdt_attach(device_t parent, device_t self, void *aux)
132 {
133 struct cpu_fdt_softc * const sc = device_private(self);
134 struct fdt_attach_args * const faa = aux;
135 const int phandle = faa->faa_phandle;
136 enum cpu_fdt_type type;
137 bus_addr_t mpidr;
138 cpuid_t cpuid;
139
140 sc->sc_dev = self;
141 sc->sc_phandle = phandle;
142
143 type = of_search_compatible(phandle, compat_data)->data;
144
145 switch (type) {
146 case ARM_CPU_ARMV7:
147 case ARM_CPU_ARMV8:
148 if (fdtbus_get_reg(phandle, 0, &mpidr, NULL) != 0) {
149 aprint_error(": missing 'reg' property\n");
150 return;
151 }
152 #ifndef __aarch64__
153 mpidr = __SHIFTOUT(mpidr, MPIDR_AFF0);
154 #endif
155 cpuid = mpidr;
156 break;
157 default:
158 cpuid = 0;
159 break;
160 }
161
162 /* Attach the CPU */
163 cpu_attach(self, cpuid);
164
165 /* Attach CPU frequency scaling provider */
166 config_found(self, faa, NULL);
167 }
168
169 #ifdef MULTIPROCESSOR
170 static register_t
171 cpu_fdt_mpstart_pa(void)
172 {
173 bool ok __diagused;
174 paddr_t pa;
175
176 ok = pmap_extract(pmap_kernel(), (vaddr_t)cpu_mpstart, &pa);
177 KASSERT(ok);
178
179 return pa;
180 }
181
182 static int
183 spintable_cpu_on(u_int cpuindex, paddr_t entry_point_address, paddr_t cpu_release_addr)
184 {
185 /*
186 * we need devmap for cpu-release-addr in advance.
187 * __HAVE_MM_MD_DIRECT_MAPPED_PHYS nor pmap didn't work at this point.
188 */
189 if (pmap_devmap_find_pa(cpu_release_addr, sizeof(paddr_t)) == NULL) {
190 aprint_error("%s: devmap for cpu-release-addr"
191 " 0x%08"PRIxPADDR" required\n", __func__, cpu_release_addr);
192 return -1;
193 } else {
194 extern struct bus_space arm_generic_bs_tag;
195 bus_space_handle_t ioh;
196
197 bus_space_map(&arm_generic_bs_tag, cpu_release_addr,
198 sizeof(paddr_t), 0, &ioh);
199 bus_space_write_4(&arm_generic_bs_tag, ioh, 0,
200 entry_point_address);
201 bus_space_unmap(&arm_generic_bs_tag, ioh, sizeof(paddr_t));
202 }
203
204 return 0;
205 }
206 #endif /* MULTIPROCESSOR */
207
208 #ifdef MULTIPROCESSOR
209 static bool
210 arm_fdt_cpu_okay(const int child)
211 {
212 const char *s;
213
214 s = fdtbus_get_string(child, "device_type");
215 if (!s || strcmp(s, "cpu") != 0)
216 return false;
217
218 s = fdtbus_get_string(child, "status");
219 if (s) {
220 if (strcmp(s, "okay") == 0)
221 return false;
222 if (strcmp(s, "disabled") == 0)
223 return of_hasprop(child, "enable-method");
224 return false;
225 } else {
226 return true;
227 }
228 }
229 #endif /* MULTIPROCESSOR */
230
231 void
232 arm_fdt_cpu_bootstrap(void)
233 {
234 #ifdef MULTIPROCESSOR
235 uint64_t mpidr, bp_mpidr;
236 u_int cpuindex;
237 int child;
238
239 const int cpus = OF_finddevice("/cpus");
240 if (cpus == -1) {
241 aprint_error("%s: no /cpus node found\n", __func__);
242 arm_cpu_max = 1;
243 return;
244 }
245
246 /* Count CPUs */
247 arm_cpu_max = 0;
248
249 /* MPIDR affinity levels of boot processor. */
250 bp_mpidr = cpu_mpidr_aff_read();
251
252 /* Boot APs */
253 cpuindex = 1;
254 for (child = OF_child(cpus); child; child = OF_peer(child)) {
255 if (!arm_fdt_cpu_okay(child))
256 continue;
257
258 arm_cpu_max++;
259 if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
260 continue;
261 if (mpidr == bp_mpidr)
262 continue; /* BP already started */
263
264 #ifdef __arm__
265 /* XXX NetBSD/arm requires all CPUs to be in the same cluster */
266 if ((mpidr & ~MPIDR_AFF0) != (bp_mpidr & ~MPIDR_AFF0))
267 continue;
268 #endif
269
270 KASSERT(cpuindex < MAXCPUS);
271 cpu_mpidr[cpuindex] = mpidr;
272 cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex],
273 sizeof(cpu_mpidr[cpuindex]));
274
275 cpuindex++;
276 }
277 #endif
278 }
279
280 void
281 arm_fdt_cpu_mpstart(void)
282 {
283 #ifdef MULTIPROCESSOR
284 uint64_t mpidr, bp_mpidr;
285 u_int cpuindex;
286 int child, ret;
287 const char *method;
288 #if NPSCI_FDT > 0
289 bool nopsci = false;
290 #endif
291
292 const int cpus = OF_finddevice("/cpus");
293 if (cpus == -1) {
294 aprint_error("%s: no /cpus node found\n", __func__);
295 return;
296 }
297
298 #if NPSCI_FDT > 0
299 if (psci_fdt_preinit() != 0)
300 nopsci = true;
301 #endif
302
303 /* MPIDR affinity levels of boot processor. */
304 bp_mpidr = cpu_mpidr_aff_read();
305
306 /* Boot APs */
307 uint32_t started = 0;
308
309 cpuindex = 1;
310 for (child = OF_child(cpus); child; child = OF_peer(child)) {
311 if (!arm_fdt_cpu_okay(child))
312 continue;
313
314 if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
315 continue;
316 if (mpidr == bp_mpidr)
317 continue; /* BP already started */
318
319 #ifdef __arm__
320 /* XXX NetBSD/arm requires all CPUs to be in the same cluster */
321 if ((mpidr & ~MPIDR_AFF0) != (bp_mpidr & ~MPIDR_AFF0))
322 continue;
323 #endif
324
325 method = fdtbus_get_string(child, "enable-method");
326 if (method == NULL)
327 continue;
328
329 if (strcmp(method, "spin-table") == 0) {
330 uint64_t data;
331 paddr_t cpu_release_addr;
332
333 if (OF_getprop(child, "cpu-release-addr", &data,
334 sizeof(data)) != sizeof(data))
335 continue;
336
337 cpu_release_addr = (paddr_t)be64toh(data);
338 ret = spintable_cpu_on(mpidr, cpu_fdt_mpstart_pa(), cpu_release_addr);
339 if (ret != 0)
340 continue;
341
342 #if NPSCI_FDT > 0
343 } else if (!nopsci && (strcmp(method, "psci") == 0)) {
344 ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0);
345 if (ret != PSCI_SUCCESS)
346 continue;
347 #endif
348 } else {
349 aprint_error("%s: %s: unsupported method\n", __func__, method);
350 continue;
351 }
352
353 started |= __BIT(cpuindex);
354 cpuindex++;
355 }
356
357 /* Wake up AP in case firmware has placed it in WFE state */
358 __asm __volatile("sev" ::: "memory");
359
360 /* Wait for APs to start */
361 for (u_int i = 0x10000000; i > 0; i--) {
362 membar_consumer();
363 if (arm_cpu_hatched == started)
364 break;
365 }
366 #endif /* MULTIPROCESSOR */
367 }
368