Annotation of src/sys/arch/arm/fdt/cpu_fdt.c, Revision 1.30.2.1
1.30.2.1! ad 1: /* $NetBSD: cpu_fdt.c,v 1.31 2020/01/12 09:29:18 mrg Exp $ */
1.1 jmcneill 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:
1.12 ryo 29: #include "opt_multiprocessor.h"
30: #include "psci_fdt.h"
31:
1.1 jmcneill 32: #include <sys/cdefs.h>
1.30.2.1! ad 33: __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.31 2020/01/12 09:29:18 mrg Exp $");
1.1 jmcneill 34:
35: #include <sys/param.h>
1.12 ryo 36: #include <sys/atomic.h>
1.1 jmcneill 37: #include <sys/bus.h>
38: #include <sys/device.h>
1.4 skrll 39: #include <sys/lwp.h>
1.1 jmcneill 40: #include <sys/systm.h>
41: #include <sys/kernel.h>
42:
43: #include <dev/fdt/fdtvar.h>
44:
1.5 ryo 45: #include <arm/armreg.h>
1.1 jmcneill 46: #include <arm/cpu.h>
1.5 ryo 47: #include <arm/cpufunc.h>
1.12 ryo 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>
1.1 jmcneill 55:
56: static int cpu_fdt_match(device_t, cfdata_t, void *);
57: static void cpu_fdt_attach(device_t, device_t, void *);
58:
1.3 jmcneill 59: enum cpu_fdt_type {
60: ARM_CPU_UP = 1,
61: ARM_CPU_ARMV7,
62: ARM_CPU_ARMV8,
63: };
64:
1.1 jmcneill 65: struct cpu_fdt_softc {
66: device_t sc_dev;
67: int sc_phandle;
68: };
69:
1.3 jmcneill 70: static const struct of_compat_data compat_data[] = {
71: { "arm,arm1176jzf-s", ARM_CPU_UP },
72:
1.6 jakllsch 73: { "arm,arm-v7", ARM_CPU_ARMV7 },
1.3 jmcneill 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:
1.11 jmcneill 82: { "arm,armv8", ARM_CPU_ARMV8 },
1.3 jmcneill 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 },
1.7 jmcneill 87:
1.3 jmcneill 88: { NULL }
89: };
90:
1.1 jmcneill 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;
1.3 jmcneill 98: const int phandle = faa->faa_phandle;
99: enum cpu_fdt_type type;
1.2 jmcneill 100: int is_compatible;
101: bus_addr_t mpidr;
102:
1.3 jmcneill 103: is_compatible = of_match_compat_data(phandle, compat_data);
1.2 jmcneill 104: if (!is_compatible)
105: return 0;
106:
1.3 jmcneill 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: default:
114: break;
115: }
1.1 jmcneill 116:
1.2 jmcneill 117: return is_compatible;
1.1 jmcneill 118: }
119:
120: static void
121: cpu_fdt_attach(device_t parent, device_t self, void *aux)
122: {
123: struct cpu_fdt_softc * const sc = device_private(self);
124: struct fdt_attach_args * const faa = aux;
1.3 jmcneill 125: const int phandle = faa->faa_phandle;
126: enum cpu_fdt_type type;
1.1 jmcneill 127: bus_addr_t mpidr;
1.3 jmcneill 128: cpuid_t cpuid;
1.30.2.1! ad 129: const uint32_t *cap_ptr;
! 130: int len;
1.1 jmcneill 131:
132: sc->sc_dev = self;
1.3 jmcneill 133: sc->sc_phandle = phandle;
134:
1.30.2.1! ad 135: cap_ptr = fdtbus_get_prop(phandle, "capacity-dmips-mhz", &len);
! 136: if (cap_ptr && len == 4) {
! 137: prop_dictionary_t dict = device_properties(self);
! 138: uint32_t capacity_dmips_mhz = be32toh(*cap_ptr);
! 139:
! 140: prop_dictionary_set_uint32(dict, "capacity_dmips_mhz",
! 141: capacity_dmips_mhz);
! 142: }
! 143:
1.3 jmcneill 144: type = of_search_compatible(phandle, compat_data)->data;
1.1 jmcneill 145:
1.3 jmcneill 146: switch (type) {
147: case ARM_CPU_ARMV7:
148: case ARM_CPU_ARMV8:
149: if (fdtbus_get_reg(phandle, 0, &mpidr, NULL) != 0) {
150: aprint_error(": missing 'reg' property\n");
151: return;
152: }
1.9 ryo 153: cpuid = mpidr;
1.3 jmcneill 154: break;
155: default:
156: cpuid = 0;
157: break;
1.1 jmcneill 158: }
159:
1.2 jmcneill 160: /* Attach the CPU */
1.3 jmcneill 161: cpu_attach(self, cpuid);
1.8 jmcneill 162:
163: /* Attach CPU frequency scaling provider */
164: config_found(self, faa, NULL);
1.1 jmcneill 165: }
1.12 ryo 166:
1.24 jmcneill 167: #if defined(MULTIPROCESSOR) && (NPSCI_FDT > 0 || defined(__aarch64__))
1.12 ryo 168: static register_t
169: cpu_fdt_mpstart_pa(void)
170: {
1.16 skrll 171: bool ok __diagused;
172: paddr_t pa;
173:
174: ok = pmap_extract(pmap_kernel(), (vaddr_t)cpu_mpstart, &pa);
175: KASSERT(ok);
176:
177: return pa;
1.12 ryo 178: }
1.24 jmcneill 179: #endif
1.12 ryo 180:
1.24 jmcneill 181: #ifdef MULTIPROCESSOR
1.13 jmcneill 182: static bool
183: arm_fdt_cpu_okay(const int child)
184: {
185: const char *s;
186:
187: s = fdtbus_get_string(child, "device_type");
188: if (!s || strcmp(s, "cpu") != 0)
189: return false;
190:
191: s = fdtbus_get_string(child, "status");
192: if (s) {
193: if (strcmp(s, "okay") == 0)
194: return false;
195: if (strcmp(s, "disabled") == 0)
196: return of_hasprop(child, "enable-method");
197: return false;
198: } else {
199: return true;
200: }
201: }
1.14 jmcneill 202: #endif /* MULTIPROCESSOR */
1.12 ryo 203:
204: void
205: arm_fdt_cpu_bootstrap(void)
206: {
207: #ifdef MULTIPROCESSOR
208: uint64_t mpidr, bp_mpidr;
209: u_int cpuindex;
1.16 skrll 210: int child;
211:
212: const int cpus = OF_finddevice("/cpus");
213: if (cpus == -1) {
214: aprint_error("%s: no /cpus node found\n", __func__);
215: arm_cpu_max = 1;
216: return;
217: }
218:
219: /* Count CPUs */
220: arm_cpu_max = 0;
221:
222: /* MPIDR affinity levels of boot processor. */
223: bp_mpidr = cpu_mpidr_aff_read();
224:
225: /* Boot APs */
226: cpuindex = 1;
227: for (child = OF_child(cpus); child; child = OF_peer(child)) {
228: if (!arm_fdt_cpu_okay(child))
229: continue;
230:
231: arm_cpu_max++;
232: if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
233: continue;
234: if (mpidr == bp_mpidr)
235: continue; /* BP already started */
236:
237: KASSERT(cpuindex < MAXCPUS);
238: cpu_mpidr[cpuindex] = mpidr;
239: cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex],
240: sizeof(cpu_mpidr[cpuindex]));
241:
242: cpuindex++;
243: }
244: #endif
245: }
246:
1.19 jmcneill 247: #ifdef MULTIPROCESSOR
1.25 jmcneill 248: static struct arm_cpu_method *
249: arm_fdt_cpu_enable_method(int phandle)
1.19 jmcneill 250: {
1.25 jmcneill 251: const char *method;
252:
253: method = fdtbus_get_string(phandle, "enable-method");
254: if (method == NULL)
255: return NULL;
256:
1.19 jmcneill 257: __link_set_decl(arm_cpu_methods, struct arm_cpu_method);
1.25 jmcneill 258: struct arm_cpu_method * const *acmp;
259: __link_set_foreach(acmp, arm_cpu_methods) {
260: if (strcmp(method, (*acmp)->acm_compat) == 0)
261: return *acmp;
1.19 jmcneill 262: }
1.25 jmcneill 263:
264: return NULL;
265: }
266:
267: static int
268: arm_fdt_cpu_enable(int phandle, struct arm_cpu_method *acm)
269: {
270: return acm->acm_enable(phandle);
1.19 jmcneill 271: }
272: #endif
273:
1.22 skrll 274: int
1.16 skrll 275: arm_fdt_cpu_mpstart(void)
276: {
1.22 skrll 277: int ret = 0;
1.16 skrll 278: #ifdef MULTIPROCESSOR
279: uint64_t mpidr, bp_mpidr;
1.19 jmcneill 280: u_int cpuindex, i;
281: int child, error;
1.25 jmcneill 282: struct arm_cpu_method *acm;
1.12 ryo 283:
284: const int cpus = OF_finddevice("/cpus");
285: if (cpus == -1) {
286: aprint_error("%s: no /cpus node found\n", __func__);
1.22 skrll 287: return 0;
1.12 ryo 288: }
289:
290: /* MPIDR affinity levels of boot processor. */
291: bp_mpidr = cpu_mpidr_aff_read();
292:
293: /* Boot APs */
294: cpuindex = 1;
295: for (child = OF_child(cpus); child; child = OF_peer(child)) {
1.13 jmcneill 296: if (!arm_fdt_cpu_okay(child))
1.12 ryo 297: continue;
1.16 skrll 298:
1.12 ryo 299: if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
300: continue;
1.18 skrll 301:
1.12 ryo 302: if (mpidr == bp_mpidr)
303: continue; /* BP already started */
304:
1.25 jmcneill 305: acm = arm_fdt_cpu_enable_method(child);
306: if (acm == NULL)
307: acm = arm_fdt_cpu_enable_method(cpus);
308: if (acm == NULL)
1.12 ryo 309: continue;
310:
1.25 jmcneill 311: error = arm_fdt_cpu_enable(child, acm);
1.19 jmcneill 312: if (error != 0) {
1.25 jmcneill 313: aprint_error("%s: failed to enable CPU %#" PRIx64 "\n", __func__, mpidr);
1.12 ryo 314: continue;
315: }
316:
1.19 jmcneill 317: /* Wake up AP in case firmware has placed it in WFE state */
318: __asm __volatile("sev" ::: "memory");
319:
320: /* Wait for AP to start */
1.21 jmcneill 321: for (i = 0x10000000; i > 0; i--) {
1.28 jmcneill 322: if (cpu_hatched_p(cpuindex))
1.19 jmcneill 323: break;
324: }
1.22 skrll 325:
326: if (i == 0) {
327: ret++;
1.19 jmcneill 328: aprint_error("cpu%d: WARNING: AP failed to start\n", cpuindex);
1.22 skrll 329: }
1.19 jmcneill 330:
1.12 ryo 331: cpuindex++;
332: }
1.19 jmcneill 333: #endif /* MULTIPROCESSOR */
1.22 skrll 334: return ret;
1.19 jmcneill 335: }
1.12 ryo 336:
1.19 jmcneill 337: static int
338: cpu_enable_nullop(int phandle)
339: {
340: return ENXIO;
341: }
342: ARM_CPU_METHOD(default, "", cpu_enable_nullop);
1.12 ryo 343:
1.19 jmcneill 344: #if defined(MULTIPROCESSOR) && NPSCI_FDT > 0
345: static int
346: cpu_enable_psci(int phandle)
347: {
348: static bool psci_probed, psci_p;
349: uint64_t mpidr;
350: int ret;
351:
352: if (!psci_probed) {
353: psci_probed = true;
354: psci_p = psci_fdt_preinit() == 0;
1.12 ryo 355: }
1.19 jmcneill 356: if (!psci_p)
357: return ENXIO;
358:
359: fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
360:
1.29 bad 361: #if !defined(AARCH64)
362: /*
1.30 bad 363: * not necessary on AARCH64. beside there it hangs the system
1.29 bad 364: * because cache ops are only functional after cpu_attach()
365: * was called.
366: */
367: cpu_dcache_wbinv_all();
368: #endif
1.19 jmcneill 369: ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0);
370: if (ret != PSCI_SUCCESS)
371: return EIO;
372:
373: return 0;
374: }
375: ARM_CPU_METHOD(psci, "psci", cpu_enable_psci);
376: #endif
377:
1.23 jmcneill 378: #if defined(MULTIPROCESSOR) && defined(__aarch64__)
379: static int
380: spintable_cpu_on(u_int cpuindex, paddr_t entry_point_address, paddr_t cpu_release_addr)
381: {
382: /*
383: * we need devmap for cpu-release-addr in advance.
384: * __HAVE_MM_MD_DIRECT_MAPPED_PHYS nor pmap didn't work at this point.
385: */
386: if (pmap_devmap_find_pa(cpu_release_addr, sizeof(paddr_t)) == NULL) {
387: aprint_error("%s: devmap for cpu-release-addr"
388: " 0x%08"PRIxPADDR" required\n", __func__, cpu_release_addr);
389: return -1;
390: } else {
391: extern struct bus_space arm_generic_bs_tag;
392: bus_space_handle_t ioh;
393:
394: bus_space_map(&arm_generic_bs_tag, cpu_release_addr,
395: sizeof(paddr_t), 0, &ioh);
396: bus_space_write_4(&arm_generic_bs_tag, ioh, 0,
397: entry_point_address);
398: bus_space_unmap(&arm_generic_bs_tag, ioh, sizeof(paddr_t));
399: }
400:
401: return 0;
402: }
403:
1.19 jmcneill 404: static int
405: cpu_enable_spin_table(int phandle)
406: {
1.20 jmcneill 407: uint64_t mpidr, addr;
1.19 jmcneill 408: int ret;
409:
410: fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
411:
1.20 jmcneill 412: if (of_getprop_uint64(phandle, "cpu-release-addr", &addr) != 0)
1.19 jmcneill 413: return ENXIO;
414:
1.20 jmcneill 415: ret = spintable_cpu_on(mpidr, cpu_fdt_mpstart_pa(), (paddr_t)addr);
1.19 jmcneill 416: if (ret != 0)
417: return EIO;
418:
419: return 0;
1.12 ryo 420: }
1.19 jmcneill 421: ARM_CPU_METHOD(spin_table, "spin-table", cpu_enable_spin_table);
422: #endif
CVSweb <webmaster@jp.NetBSD.org>