1 1.44 riastrad /* $NetBSD: cpu_fdt.c,v 1.44 2024/05/10 14:42:21 riastradh Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2017 Jared McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 1.1 jmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 1.1 jmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 1.1 jmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 1.1 jmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 1.1 jmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 1.1 jmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 1.1 jmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 1.1 jmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 jmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 jmcneill * SUCH DAMAGE. 27 1.1 jmcneill */ 28 1.1 jmcneill 29 1.12 ryo #include "opt_multiprocessor.h" 30 1.12 ryo #include "psci_fdt.h" 31 1.12 ryo 32 1.1 jmcneill #include <sys/cdefs.h> 33 1.44 riastrad __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.44 2024/05/10 14:42:21 riastradh Exp $"); 34 1.1 jmcneill 35 1.1 jmcneill #include <sys/param.h> 36 1.12 ryo #include <sys/atomic.h> 37 1.1 jmcneill #include <sys/bus.h> 38 1.1 jmcneill #include <sys/device.h> 39 1.4 skrll #include <sys/lwp.h> 40 1.1 jmcneill #include <sys/systm.h> 41 1.1 jmcneill #include <sys/kernel.h> 42 1.1 jmcneill 43 1.1 jmcneill #include <dev/fdt/fdtvar.h> 44 1.1 jmcneill 45 1.5 ryo #include <arm/armreg.h> 46 1.1 jmcneill #include <arm/cpu.h> 47 1.5 ryo #include <arm/cpufunc.h> 48 1.34 skrll #include <arm/cpuvar.h> 49 1.12 ryo #include <arm/locore.h> 50 1.12 ryo 51 1.12 ryo #include <arm/arm/psci.h> 52 1.12 ryo #include <arm/fdt/arm_fdtvar.h> 53 1.12 ryo #include <arm/fdt/psci_fdtvar.h> 54 1.12 ryo 55 1.12 ryo #include <uvm/uvm_extern.h> 56 1.1 jmcneill 57 1.1 jmcneill static int cpu_fdt_match(device_t, cfdata_t, void *); 58 1.1 jmcneill static void cpu_fdt_attach(device_t, device_t, void *); 59 1.1 jmcneill 60 1.43 pho CFATTACH_DECL2_NEW(cpu_fdt, 0, 61 1.43 pho cpu_fdt_match, cpu_fdt_attach, NULL, NULL, 62 1.43 pho cpu_rescan, cpu_childdetached); 63 1.1 jmcneill 64 1.1 jmcneill static int 65 1.1 jmcneill cpu_fdt_match(device_t parent, cfdata_t cf, void *aux) 66 1.1 jmcneill { 67 1.1 jmcneill struct fdt_attach_args * const faa = aux; 68 1.3 jmcneill const int phandle = faa->faa_phandle; 69 1.33 jmcneill const char *device_type; 70 1.2 jmcneill 71 1.33 jmcneill device_type = fdtbus_get_string(phandle, "device_type"); 72 1.1 jmcneill 73 1.33 jmcneill return device_type != NULL && strcmp(device_type, "cpu") == 0; 74 1.1 jmcneill } 75 1.1 jmcneill 76 1.1 jmcneill static void 77 1.1 jmcneill cpu_fdt_attach(device_t parent, device_t self, void *aux) 78 1.1 jmcneill { 79 1.1 jmcneill struct fdt_attach_args * const faa = aux; 80 1.3 jmcneill const int phandle = faa->faa_phandle; 81 1.33 jmcneill bus_addr_t cpuid; 82 1.31 mrg const uint32_t *cap_ptr; 83 1.31 mrg int len; 84 1.1 jmcneill 85 1.31 mrg cap_ptr = fdtbus_get_prop(phandle, "capacity-dmips-mhz", &len); 86 1.31 mrg if (cap_ptr && len == 4) { 87 1.31 mrg prop_dictionary_t dict = device_properties(self); 88 1.31 mrg uint32_t capacity_dmips_mhz = be32toh(*cap_ptr); 89 1.31 mrg 90 1.31 mrg prop_dictionary_set_uint32(dict, "capacity_dmips_mhz", 91 1.31 mrg capacity_dmips_mhz); 92 1.31 mrg } 93 1.31 mrg 94 1.33 jmcneill if (fdtbus_get_reg(phandle, 0, &cpuid, NULL) != 0) 95 1.3 jmcneill cpuid = 0; 96 1.1 jmcneill 97 1.2 jmcneill /* Attach the CPU */ 98 1.3 jmcneill cpu_attach(self, cpuid); 99 1.8 jmcneill 100 1.8 jmcneill /* Attach CPU frequency scaling provider */ 101 1.44 riastrad config_found(self, faa, NULL, CFARGS(.iattr = "cpu")); 102 1.1 jmcneill } 103 1.12 ryo 104 1.24 jmcneill #if defined(MULTIPROCESSOR) && (NPSCI_FDT > 0 || defined(__aarch64__)) 105 1.12 ryo static register_t 106 1.12 ryo cpu_fdt_mpstart_pa(void) 107 1.12 ryo { 108 1.16 skrll bool ok __diagused; 109 1.16 skrll paddr_t pa; 110 1.16 skrll 111 1.16 skrll ok = pmap_extract(pmap_kernel(), (vaddr_t)cpu_mpstart, &pa); 112 1.16 skrll KASSERT(ok); 113 1.16 skrll 114 1.16 skrll return pa; 115 1.12 ryo } 116 1.24 jmcneill #endif 117 1.12 ryo 118 1.24 jmcneill #ifdef MULTIPROCESSOR 119 1.13 jmcneill static bool 120 1.13 jmcneill arm_fdt_cpu_okay(const int child) 121 1.13 jmcneill { 122 1.13 jmcneill const char *s; 123 1.13 jmcneill 124 1.13 jmcneill s = fdtbus_get_string(child, "device_type"); 125 1.13 jmcneill if (!s || strcmp(s, "cpu") != 0) 126 1.13 jmcneill return false; 127 1.13 jmcneill 128 1.13 jmcneill s = fdtbus_get_string(child, "status"); 129 1.13 jmcneill if (s) { 130 1.13 jmcneill if (strcmp(s, "okay") == 0) 131 1.13 jmcneill return false; 132 1.13 jmcneill if (strcmp(s, "disabled") == 0) 133 1.13 jmcneill return of_hasprop(child, "enable-method"); 134 1.13 jmcneill return false; 135 1.13 jmcneill } else { 136 1.13 jmcneill return true; 137 1.13 jmcneill } 138 1.13 jmcneill } 139 1.14 jmcneill #endif /* MULTIPROCESSOR */ 140 1.12 ryo 141 1.12 ryo void 142 1.12 ryo arm_fdt_cpu_bootstrap(void) 143 1.12 ryo { 144 1.12 ryo #ifdef MULTIPROCESSOR 145 1.12 ryo uint64_t mpidr, bp_mpidr; 146 1.12 ryo u_int cpuindex; 147 1.16 skrll int child; 148 1.16 skrll 149 1.16 skrll const int cpus = OF_finddevice("/cpus"); 150 1.16 skrll if (cpus == -1) { 151 1.16 skrll aprint_error("%s: no /cpus node found\n", __func__); 152 1.16 skrll arm_cpu_max = 1; 153 1.16 skrll return; 154 1.16 skrll } 155 1.16 skrll 156 1.16 skrll /* Count CPUs */ 157 1.16 skrll arm_cpu_max = 0; 158 1.16 skrll 159 1.16 skrll /* MPIDR affinity levels of boot processor. */ 160 1.16 skrll bp_mpidr = cpu_mpidr_aff_read(); 161 1.16 skrll 162 1.37 skrll /* Add APs to cpu_mpidr array */ 163 1.16 skrll cpuindex = 1; 164 1.16 skrll for (child = OF_child(cpus); child; child = OF_peer(child)) { 165 1.16 skrll if (!arm_fdt_cpu_okay(child)) 166 1.16 skrll continue; 167 1.16 skrll 168 1.16 skrll arm_cpu_max++; 169 1.16 skrll if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0) 170 1.16 skrll continue; 171 1.16 skrll if (mpidr == bp_mpidr) 172 1.16 skrll continue; /* BP already started */ 173 1.16 skrll 174 1.16 skrll KASSERT(cpuindex < MAXCPUS); 175 1.16 skrll cpu_mpidr[cpuindex] = mpidr; 176 1.16 skrll cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex], 177 1.16 skrll sizeof(cpu_mpidr[cpuindex])); 178 1.16 skrll 179 1.16 skrll cpuindex++; 180 1.16 skrll } 181 1.16 skrll #endif 182 1.16 skrll } 183 1.16 skrll 184 1.19 jmcneill #ifdef MULTIPROCESSOR 185 1.25 jmcneill static struct arm_cpu_method * 186 1.36 jmcneill arm_fdt_cpu_enable_method_byname(const char *method) 187 1.19 jmcneill { 188 1.19 jmcneill __link_set_decl(arm_cpu_methods, struct arm_cpu_method); 189 1.25 jmcneill struct arm_cpu_method * const *acmp; 190 1.36 jmcneill 191 1.25 jmcneill __link_set_foreach(acmp, arm_cpu_methods) { 192 1.25 jmcneill if (strcmp(method, (*acmp)->acm_compat) == 0) 193 1.25 jmcneill return *acmp; 194 1.19 jmcneill } 195 1.25 jmcneill 196 1.25 jmcneill return NULL; 197 1.25 jmcneill } 198 1.25 jmcneill 199 1.36 jmcneill static struct arm_cpu_method * 200 1.36 jmcneill arm_fdt_cpu_enable_method(int phandle) 201 1.36 jmcneill { 202 1.36 jmcneill const char *method; 203 1.36 jmcneill 204 1.36 jmcneill method = fdtbus_get_string(phandle, "enable-method"); 205 1.36 jmcneill if (method == NULL) 206 1.36 jmcneill return NULL; 207 1.36 jmcneill 208 1.36 jmcneill return arm_fdt_cpu_enable_method_byname(method); 209 1.36 jmcneill } 210 1.36 jmcneill 211 1.25 jmcneill static int 212 1.25 jmcneill arm_fdt_cpu_enable(int phandle, struct arm_cpu_method *acm) 213 1.25 jmcneill { 214 1.25 jmcneill return acm->acm_enable(phandle); 215 1.19 jmcneill } 216 1.19 jmcneill #endif 217 1.19 jmcneill 218 1.22 skrll int 219 1.16 skrll arm_fdt_cpu_mpstart(void) 220 1.16 skrll { 221 1.22 skrll int ret = 0; 222 1.16 skrll #ifdef MULTIPROCESSOR 223 1.16 skrll uint64_t mpidr, bp_mpidr; 224 1.19 jmcneill u_int cpuindex, i; 225 1.19 jmcneill int child, error; 226 1.25 jmcneill struct arm_cpu_method *acm; 227 1.12 ryo 228 1.12 ryo const int cpus = OF_finddevice("/cpus"); 229 1.12 ryo if (cpus == -1) { 230 1.12 ryo aprint_error("%s: no /cpus node found\n", __func__); 231 1.22 skrll return 0; 232 1.12 ryo } 233 1.12 ryo 234 1.12 ryo /* MPIDR affinity levels of boot processor. */ 235 1.12 ryo bp_mpidr = cpu_mpidr_aff_read(); 236 1.12 ryo 237 1.12 ryo /* Boot APs */ 238 1.12 ryo cpuindex = 1; 239 1.12 ryo for (child = OF_child(cpus); child; child = OF_peer(child)) { 240 1.13 jmcneill if (!arm_fdt_cpu_okay(child)) 241 1.12 ryo continue; 242 1.16 skrll 243 1.12 ryo if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0) 244 1.12 ryo continue; 245 1.18 skrll 246 1.12 ryo if (mpidr == bp_mpidr) 247 1.12 ryo continue; /* BP already started */ 248 1.12 ryo 249 1.25 jmcneill acm = arm_fdt_cpu_enable_method(child); 250 1.25 jmcneill if (acm == NULL) 251 1.25 jmcneill acm = arm_fdt_cpu_enable_method(cpus); 252 1.25 jmcneill if (acm == NULL) 253 1.36 jmcneill acm = arm_fdt_cpu_enable_method_byname("psci"); 254 1.36 jmcneill if (acm == NULL) 255 1.12 ryo continue; 256 1.12 ryo 257 1.25 jmcneill error = arm_fdt_cpu_enable(child, acm); 258 1.19 jmcneill if (error != 0) { 259 1.32 skrll aprint_error("%s: failed to enable CPU %#" PRIx64 "\n", 260 1.32 skrll __func__, mpidr); 261 1.12 ryo continue; 262 1.12 ryo } 263 1.12 ryo 264 1.19 jmcneill /* Wake up AP in case firmware has placed it in WFE state */ 265 1.38 skrll sev(); 266 1.19 jmcneill 267 1.19 jmcneill /* Wait for AP to start */ 268 1.21 jmcneill for (i = 0x10000000; i > 0; i--) { 269 1.28 jmcneill if (cpu_hatched_p(cpuindex)) 270 1.19 jmcneill break; 271 1.19 jmcneill } 272 1.22 skrll 273 1.22 skrll if (i == 0) { 274 1.22 skrll ret++; 275 1.19 jmcneill aprint_error("cpu%d: WARNING: AP failed to start\n", cpuindex); 276 1.22 skrll } 277 1.19 jmcneill 278 1.12 ryo cpuindex++; 279 1.12 ryo } 280 1.19 jmcneill #endif /* MULTIPROCESSOR */ 281 1.22 skrll return ret; 282 1.19 jmcneill } 283 1.12 ryo 284 1.19 jmcneill static int 285 1.19 jmcneill cpu_enable_nullop(int phandle) 286 1.19 jmcneill { 287 1.19 jmcneill return ENXIO; 288 1.19 jmcneill } 289 1.19 jmcneill ARM_CPU_METHOD(default, "", cpu_enable_nullop); 290 1.12 ryo 291 1.19 jmcneill #if defined(MULTIPROCESSOR) && NPSCI_FDT > 0 292 1.19 jmcneill static int 293 1.19 jmcneill cpu_enable_psci(int phandle) 294 1.19 jmcneill { 295 1.19 jmcneill static bool psci_probed, psci_p; 296 1.19 jmcneill uint64_t mpidr; 297 1.19 jmcneill int ret; 298 1.19 jmcneill 299 1.19 jmcneill if (!psci_probed) { 300 1.19 jmcneill psci_probed = true; 301 1.19 jmcneill psci_p = psci_fdt_preinit() == 0; 302 1.12 ryo } 303 1.19 jmcneill if (!psci_p) 304 1.19 jmcneill return ENXIO; 305 1.19 jmcneill 306 1.19 jmcneill fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 307 1.19 jmcneill 308 1.29 bad #if !defined(AARCH64) 309 1.29 bad /* 310 1.30 bad * not necessary on AARCH64. beside there it hangs the system 311 1.29 bad * because cache ops are only functional after cpu_attach() 312 1.29 bad * was called. 313 1.29 bad */ 314 1.29 bad cpu_dcache_wbinv_all(); 315 1.29 bad #endif 316 1.19 jmcneill ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0); 317 1.19 jmcneill if (ret != PSCI_SUCCESS) 318 1.19 jmcneill return EIO; 319 1.19 jmcneill 320 1.19 jmcneill return 0; 321 1.19 jmcneill } 322 1.19 jmcneill ARM_CPU_METHOD(psci, "psci", cpu_enable_psci); 323 1.19 jmcneill #endif 324 1.19 jmcneill 325 1.23 jmcneill #if defined(MULTIPROCESSOR) && defined(__aarch64__) 326 1.23 jmcneill static int 327 1.41 jmcneill spintable_cpu_on(const int phandle, u_int cpuindex, 328 1.41 jmcneill paddr_t entry_point_address, paddr_t cpu_release_addr) 329 1.23 jmcneill { 330 1.23 jmcneill /* 331 1.23 jmcneill * we need devmap for cpu-release-addr in advance. 332 1.35 skrll * __HAVE_MM_MD_DIRECT_MAPPED_PHYS nor pmap work at this point. 333 1.23 jmcneill */ 334 1.23 jmcneill if (pmap_devmap_find_pa(cpu_release_addr, sizeof(paddr_t)) == NULL) { 335 1.23 jmcneill aprint_error("%s: devmap for cpu-release-addr" 336 1.23 jmcneill " 0x%08"PRIxPADDR" required\n", __func__, cpu_release_addr); 337 1.23 jmcneill return -1; 338 1.23 jmcneill } else { 339 1.23 jmcneill extern struct bus_space arm_generic_bs_tag; 340 1.23 jmcneill bus_space_handle_t ioh; 341 1.23 jmcneill 342 1.41 jmcneill const int parent = OF_parent(phandle); 343 1.41 jmcneill const int addr_cells = fdtbus_get_addr_cells(parent); 344 1.41 jmcneill 345 1.23 jmcneill bus_space_map(&arm_generic_bs_tag, cpu_release_addr, 346 1.23 jmcneill sizeof(paddr_t), 0, &ioh); 347 1.41 jmcneill if (addr_cells == 1) { 348 1.41 jmcneill bus_space_write_4(&arm_generic_bs_tag, ioh, 0, 349 1.41 jmcneill entry_point_address); 350 1.41 jmcneill } else { 351 1.41 jmcneill bus_space_write_8(&arm_generic_bs_tag, ioh, 0, 352 1.41 jmcneill entry_point_address); 353 1.41 jmcneill } 354 1.23 jmcneill bus_space_unmap(&arm_generic_bs_tag, ioh, sizeof(paddr_t)); 355 1.23 jmcneill } 356 1.23 jmcneill 357 1.23 jmcneill return 0; 358 1.23 jmcneill } 359 1.23 jmcneill 360 1.19 jmcneill static int 361 1.19 jmcneill cpu_enable_spin_table(int phandle) 362 1.19 jmcneill { 363 1.20 jmcneill uint64_t mpidr, addr; 364 1.19 jmcneill int ret; 365 1.19 jmcneill 366 1.19 jmcneill fdtbus_get_reg64(phandle, 0, &mpidr, NULL); 367 1.19 jmcneill 368 1.20 jmcneill if (of_getprop_uint64(phandle, "cpu-release-addr", &addr) != 0) 369 1.19 jmcneill return ENXIO; 370 1.19 jmcneill 371 1.41 jmcneill ret = spintable_cpu_on(phandle, mpidr, cpu_fdt_mpstart_pa(), 372 1.41 jmcneill (paddr_t)addr); 373 1.19 jmcneill if (ret != 0) 374 1.19 jmcneill return EIO; 375 1.19 jmcneill 376 1.19 jmcneill return 0; 377 1.12 ryo } 378 1.19 jmcneill ARM_CPU_METHOD(spin_table, "spin-table", cpu_enable_spin_table); 379 1.19 jmcneill #endif 380