cpu_fdt.c revision 1.14 1 /* $NetBSD: cpu_fdt.c,v 1.14 2018/09/13 12:53:00 jmcneill 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.14 2018/09/13 12:53:00 jmcneill 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 #ifdef __aarch64__
174 extern void aarch64_mpstart(void);
175 return (register_t)aarch64_kern_vtophys((vaddr_t)aarch64_mpstart);
176 #else
177 extern void cortex_mpstart(void);
178 return (register_t)cortex_mpstart;
179 #endif
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, ret;
238 const char *method;
239
240 const int cpus = OF_finddevice("/cpus");
241 if (cpus == -1) {
242 aprint_error("%s: no /cpus node found\n", __func__);
243 arm_cpu_max = 1;
244 return;
245 }
246
247 /* Count CPUs */
248 arm_cpu_max = 0;
249 for (child = OF_child(cpus); child; child = OF_peer(child))
250 if (arm_fdt_cpu_okay(child))
251 arm_cpu_max++;
252
253 #if NPSCI_FDT > 0
254 if (psci_fdt_preinit() != 0)
255 return;
256 #endif
257
258 /* MPIDR affinity levels of boot processor. */
259 bp_mpidr = cpu_mpidr_aff_read();
260
261 /* Boot APs */
262 uint32_t started = 0;
263 cpuindex = 1;
264 for (child = OF_child(cpus); child; child = OF_peer(child)) {
265 if (!arm_fdt_cpu_okay(child))
266 continue;
267 if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
268 continue;
269 if (mpidr == bp_mpidr)
270 continue; /* BP already started */
271
272 #ifdef __arm__
273 /* XXX NetBSD/arm requires all CPUs to be in the same cluster */
274 if ((mpidr & ~MPIDR_AFF0) != (bp_mpidr & ~MPIDR_AFF0))
275 continue;
276 #endif
277
278 #ifdef __aarch64__
279 KASSERT(cpuindex < MAXCPUS);
280 cpu_mpidr[cpuindex] = mpidr;
281 cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex], sizeof(cpu_mpidr[cpuindex]));
282 #endif
283
284 method = fdtbus_get_string(child, "enable-method");
285 if (method == NULL)
286 continue;
287
288 if (strcmp(method, "spin-table") == 0) {
289 uint64_t data;
290 paddr_t cpu_release_addr;
291
292 if (OF_getprop(child, "cpu-release-addr", &data,
293 sizeof(data)) != sizeof(data))
294 continue;
295
296 cpu_release_addr = (paddr_t)be64toh(data);
297 ret = spintable_cpu_on(mpidr, cpu_fdt_mpstart_pa(), cpu_release_addr);
298 if (ret != 0)
299 continue;
300
301 #if NPSCI_FDT > 0
302 } else if (strcmp(method, "psci") == 0) {
303 ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0);
304 if (ret != PSCI_SUCCESS)
305 continue;
306 #endif
307 } else {
308 aprint_error("%s: %s: unsupported method\n", __func__, method);
309 continue;
310 }
311
312 started |= __BIT(cpuindex);
313 cpuindex++;
314 }
315
316 /* Wake up AP in case firmware has placed it in WFE state */
317 __asm __volatile("sev" ::: "memory");
318
319 /* Wait for APs to start */
320 for (u_int i = 0x10000000; i > 0; i--) {
321 membar_consumer();
322 if (arm_cpu_hatched == started)
323 break;
324 }
325 #endif /* MULTIPROCESSOR */
326 }
327