1 /* $NetBSD: cpu_fdt.c,v 1.30 2019/11/01 13:22:08 bad 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.30 2019/11/01 13:22:08 bad 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 default: 114 break; 115 } 116 117 return is_compatible; 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; 125 const int phandle = faa->faa_phandle; 126 enum cpu_fdt_type type; 127 bus_addr_t mpidr; 128 cpuid_t cpuid; 129 130 sc->sc_dev = self; 131 sc->sc_phandle = phandle; 132 133 type = of_search_compatible(phandle, compat_data)->data; 134 135 switch (type) { 136 case ARM_CPU_ARMV7: 137 case ARM_CPU_ARMV8: 138 if (fdtbus_get_reg(phandle, 0, &mpidr, NULL) != 0) { 139 aprint_error(": missing 'reg' property\n"); 140 return; 141 } 142 cpuid = mpidr; 143 break; 144 default: 145 cpuid = 0; 146 break; 147 } 148 149 /* Attach the CPU */ 150 cpu_attach(self, cpuid); 151 152 /* Attach CPU frequency scaling provider */ 153 config_found(self, faa, NULL); 154 } 155 156 #if defined(MULTIPROCESSOR) && (NPSCI_FDT > 0 || defined(__aarch64__)) 157 static register_t 158 cpu_fdt_mpstart_pa(void) 159 { 160 bool ok __diagused; 161 paddr_t pa; 162 163 ok = pmap_extract(pmap_kernel(), (vaddr_t)cpu_mpstart, &pa); 164 KASSERT(ok); 165 166 return pa; 167 } 168 #endif 169 170 #ifdef MULTIPROCESSOR 171 static bool 172 arm_fdt_cpu_okay(const int child) 173 { 174 const char *s; 175 176 s = fdtbus_get_string(child, "device_type"); 177 if (!s || strcmp(s, "cpu") != 0) 178 return false; 179 180 s = fdtbus_get_string(child, "status"); 181 if (s) { 182 if (strcmp(s, "okay") == 0) 183 return false; 184 if (strcmp(s, "disabled") == 0) 185 return of_hasprop(child, "enable-method"); 186 return false; 187 } else { 188 return true; 189 } 190 } 191 #endif /* MULTIPROCESSOR */ 192 193 void 194 arm_fdt_cpu_bootstrap(void) 195 { 196 #ifdef MULTIPROCESSOR 197 uint64_t mpidr, bp_mpidr; 198 u_int cpuindex; 199 int child; 200 201 const int cpus = OF_finddevice("/cpus"); 202 if (cpus == -1) { 203 aprint_error("%s: no /cpus node found\n", __func__); 204 arm_cpu_max = 1; 205 return; 206 } 207 208 /* Count CPUs */ 209 arm_cpu_max = 0; 210 211 /* MPIDR affinity levels of boot processor. */ 212 bp_mpidr = cpu_mpidr_aff_read(); 213 214 /* Boot APs */ 215 cpuindex = 1; 216 for (child = OF_child(cpus); child; child = OF_peer(child)) { 217 if (!arm_fdt_cpu_okay(child)) 218 continue; 219 220 arm_cpu_max++; 221 if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0) 222 continue; 223 if (mpidr == bp_mpidr) 224 continue; /* BP already started */ 225 226 KASSERT(cpuindex < MAXCPUS); 227 cpu_mpidr[cpuindex] = mpidr; 228 cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex], 229 sizeof(cpu_mpidr[cpuindex])); 230 231 cpuindex++; 232 } 233 #endif 234 } 235 236 #ifdef MULTIPROCESSOR 237 static struct arm_cpu_method * 238 arm_fdt_cpu_enable_method(int phandle) 239 { 240 const char *method; 241 242 method = fdtbus_get_string(phandle, "enable-method"); 243 if (method == NULL) 244 return NULL; 245 246 __link_set_decl(arm_cpu_methods, struct arm_cpu_method); 247 struct arm_cpu_method * const *acmp; 248 __link_set_foreach(acmp, arm_cpu_methods) { 249 if (strcmp(method, (*acmp)->acm_compat) == 0) 250 return *acmp; 251 } 252 253 return NULL; 254 } 255 256 static int 257 arm_fdt_cpu_enable(int phandle, struct arm_cpu_method *acm) 258 { 259 return acm->acm_enable(phandle); 260 } 261 #endif 262 263 int 264 arm_fdt_cpu_mpstart(void) 265 { 266 int ret = 0; 267 #ifdef MULTIPROCESSOR 268 uint64_t mpidr, bp_mpidr; 269 u_int cpuindex, i; 270 int child, error; 271 struct arm_cpu_method *acm; 272 273 const int cpus = OF_finddevice("/cpus"); 274 if (cpus == -1) { 275 aprint_error("%s: no /cpus node found\n", __func__); 276 return 0; 277 } 278 279 /* MPIDR affinity levels of boot processor. */ 280 bp_mpidr = cpu_mpidr_aff_read(); 281 282 /* Boot APs */ 283 cpuindex = 1; 284 for (child = OF_child(cpus); child; child = OF_peer(child)) { 285 if (!arm_fdt_cpu_okay(child)) 286 continue; 287 288 if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0) 289 continue; 290 291 if (mpidr == bp_mpidr) 292 continue; /* BP already started */ 293 294 acm = arm_fdt_cpu_enable_method(child); 295 if (acm == NULL) 296 acm = arm_fdt_cpu_enable_method(cpus); 297 if (acm == NULL) 298 continue; 299 300 error = arm_fdt_cpu_enable(child, acm); 301 if (error != 0) { 302 aprint_error("%s: failed to enable CPU %#" PRIx64 "\n", __func__, mpidr); 303 continue; 304 } 305 306 /* Wake up AP in case firmware has placed it in WFE state */ 307 __asm __volatile("sev" ::: "memory"); 308 309 /* Wait for AP to start */ 310 for (i = 0x10000000; i > 0; i--) { 311 if (cpu_hatched_p(cpuindex)) 312 break; 313 } 314 315 if (i == 0) { 316 ret++; 317 aprint_error("cpu%d: WARNING: AP failed to start\n", cpuindex); 318 } 319 320 cpuindex++; 321 } 322 #endif /* MULTIPROCESSOR */ 323 return ret; 324 } 325 326 static int 327 cpu_enable_nullop(int phandle) 328 { 329 return ENXIO; 330 } 331 ARM_CPU_METHOD(default, "", cpu_enable_nullop); 332 333 #if defined(MULTIPROCESSOR) && NPSCI_FDT > 0 334 static int 335 cpu_enable_psci(int phandle) 336 { 337 static bool psci_probed, psci_p; 338 uint64_t mpidr; 339 int ret; 340 341 if (!psci_probed) { 342 psci_probed = true; 343 psci_p = psci_fdt_preinit() == 0; 344 } 345 if (!psci_p) 346 return ENXIO; 347 348 fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 349 350 #if !defined(AARCH64) 351 /* 352 * not necessary on AARCH64. beside there it hangs the system 353 * because cache ops are only functional after cpu_attach() 354 * was called. 355 */ 356 cpu_dcache_wbinv_all(); 357 #endif 358 ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0); 359 if (ret != PSCI_SUCCESS) 360 return EIO; 361 362 return 0; 363 } 364 ARM_CPU_METHOD(psci, "psci", cpu_enable_psci); 365 #endif 366 367 #if defined(MULTIPROCESSOR) && defined(__aarch64__) 368 static int 369 spintable_cpu_on(u_int cpuindex, paddr_t entry_point_address, paddr_t cpu_release_addr) 370 { 371 /* 372 * we need devmap for cpu-release-addr in advance. 373 * __HAVE_MM_MD_DIRECT_MAPPED_PHYS nor pmap didn't work at this point. 374 */ 375 if (pmap_devmap_find_pa(cpu_release_addr, sizeof(paddr_t)) == NULL) { 376 aprint_error("%s: devmap for cpu-release-addr" 377 " 0x%08"PRIxPADDR" required\n", __func__, cpu_release_addr); 378 return -1; 379 } else { 380 extern struct bus_space arm_generic_bs_tag; 381 bus_space_handle_t ioh; 382 383 bus_space_map(&arm_generic_bs_tag, cpu_release_addr, 384 sizeof(paddr_t), 0, &ioh); 385 bus_space_write_4(&arm_generic_bs_tag, ioh, 0, 386 entry_point_address); 387 bus_space_unmap(&arm_generic_bs_tag, ioh, sizeof(paddr_t)); 388 } 389 390 return 0; 391 } 392 393 static int 394 cpu_enable_spin_table(int phandle) 395 { 396 uint64_t mpidr, addr; 397 int ret; 398 399 fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 400 401 if (of_getprop_uint64(phandle, "cpu-release-addr", &addr) != 0) 402 return ENXIO; 403 404 ret = spintable_cpu_on(mpidr, cpu_fdt_mpstart_pa(), (paddr_t)addr); 405 if (ret != 0) 406 return EIO; 407 408 return 0; 409 } 410 ARM_CPU_METHOD(spin_table, "spin-table", cpu_enable_spin_table); 411 #endif 412