1 /* $OpenBSD: mainbus.c,v 1.29 2024/01/29 14:52:25 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se> 4 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/kernel.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <machine/fdt.h> 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/fdt.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/ofw_thermal.h> 30 31 #include <arm64/arm64/arm64var.h> 32 #include <arm64/dev/mainbus.h> 33 34 int mainbus_match(struct device *, void *, void *); 35 void mainbus_attach(struct device *, struct device *, void *); 36 37 void mainbus_attach_node(struct device *, int, cfmatch_t); 38 int mainbus_match_status(struct device *, void *, void *); 39 void mainbus_attach_cpus(struct device *, cfmatch_t); 40 int mainbus_match_primary(struct device *, void *, void *); 41 int mainbus_match_secondary(struct device *, void *, void *); 42 void mainbus_attach_psci(struct device *); 43 void mainbus_attach_efi(struct device *); 44 void mainbus_attach_apm(struct device *); 45 void mainbus_attach_framebuffer(struct device *); 46 void mainbus_attach_firmware(struct device *); 47 void mainbus_attach_resvmem(struct device *); 48 49 struct mainbus_softc { 50 struct device sc_dev; 51 int sc_node; 52 bus_space_tag_t sc_iot; 53 bus_dma_tag_t sc_dmat; 54 int sc_acells; 55 int sc_scells; 56 int *sc_ranges; 57 int sc_rangeslen; 58 int sc_early; 59 int sc_early_nodes[64]; 60 }; 61 62 const struct cfattach mainbus_ca = { 63 sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL, 64 config_activate_children 65 }; 66 67 struct cfdriver mainbus_cd = { 68 NULL, "mainbus", DV_DULL 69 }; 70 71 struct machine_bus_dma_tag mainbus_dma_tag = { 72 NULL, 73 0, 74 _dmamap_create, 75 _dmamap_destroy, 76 _dmamap_load, 77 _dmamap_load_mbuf, 78 _dmamap_load_uio, 79 _dmamap_load_raw, 80 _dmamap_load_buffer, 81 _dmamap_unload, 82 _dmamap_sync, 83 _dmamem_alloc, 84 _dmamem_alloc_range, 85 _dmamem_free, 86 _dmamem_map, 87 _dmamem_unmap, 88 _dmamem_mmap, 89 }; 90 91 /* 92 * Mainbus takes care of FDT and non-FDT machines, so we 93 * always attach. 94 */ 95 int 96 mainbus_match(struct device *parent, void *cfdata, void *aux) 97 { 98 return (1); 99 } 100 101 void agtimer_init(void); 102 103 void 104 mainbus_attach(struct device *parent, struct device *self, void *aux) 105 { 106 struct mainbus_softc *sc = (struct mainbus_softc *)self; 107 char prop[128]; 108 int node, len; 109 110 arm_intr_init_fdt(); 111 agtimer_init(); 112 113 sc->sc_node = OF_peer(0); 114 sc->sc_iot = &arm64_bs_tag; 115 sc->sc_dmat = &mainbus_dma_tag; 116 sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 117 sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 118 119 len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop)); 120 if (len > 0) { 121 printf(": %s\n", prop); 122 hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 123 if (hw_prod) 124 strlcpy(hw_prod, prop, len); 125 } else 126 printf(": unknown model\n"); 127 128 len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop)); 129 if (len > 0) { 130 hw_serial = malloc(len, M_DEVBUF, M_NOWAIT); 131 if (hw_serial) 132 strlcpy(hw_serial, prop, len); 133 } 134 135 mainbus_attach_psci(self); 136 mainbus_attach_efi(self); 137 138 /* Attach primary CPU first. */ 139 mainbus_attach_cpus(self, mainbus_match_primary); 140 141 /* Attach secondary CPUs. */ 142 mainbus_attach_cpus(self, mainbus_match_secondary); 143 144 mainbus_attach_firmware(self); 145 mainbus_attach_resvmem(self); 146 147 sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 148 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 149 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 150 OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 151 sc->sc_rangeslen); 152 } 153 154 mainbus_attach_apm(self); 155 156 /* Scan the whole tree. */ 157 for (sc->sc_early = 2; sc->sc_early >= 0; sc->sc_early--) { 158 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 159 mainbus_attach_node(self, node, NULL); 160 } 161 sc->sc_early = 0; 162 163 /* 164 * Delay attaching the framebuffer to give other drivers a 165 * chance to claim it. 166 */ 167 config_mountroot(self, mainbus_attach_framebuffer); 168 169 thermal_init(); 170 } 171 172 int 173 mainbus_print(void *aux, const char *pnp) 174 { 175 struct fdt_attach_args *fa = aux; 176 char buf[32]; 177 178 if (!pnp) 179 return (QUIET); 180 181 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 182 strcmp(buf, "disabled") == 0) 183 return (QUIET); 184 185 if (OF_getprop(fa->fa_node, "name", buf, sizeof(buf)) > 0) { 186 buf[sizeof(buf) - 1] = 0; 187 if (strcmp(buf, "aliases") == 0 || 188 strcmp(buf, "chosen") == 0 || 189 strcmp(buf, "cpus") == 0 || 190 strcmp(buf, "memory") == 0 || 191 strcmp(buf, "reserved-memory") == 0 || 192 strcmp(buf, "thermal-zones") == 0 || 193 strncmp(buf, "__", 2) == 0) 194 return (QUIET); 195 printf("\"%s\"", buf); 196 } else 197 printf("node %u", fa->fa_node); 198 199 printf(" at %s", pnp); 200 201 return (UNCONF); 202 } 203 204 /* 205 * Look for a driver that wants to be attached to this node. 206 */ 207 void 208 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 209 { 210 struct mainbus_softc *sc = (struct mainbus_softc *)self; 211 struct fdt_attach_args fa; 212 int i, len, line; 213 uint32_t *cell, *reg; 214 struct device *child; 215 cfprint_t print = NULL; 216 217 /* Skip if already attached early. */ 218 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 219 if (sc->sc_early_nodes[i] == node) 220 return; 221 if (sc->sc_early_nodes[i] == 0) 222 break; 223 } 224 225 memset(&fa, 0, sizeof(fa)); 226 fa.fa_name = ""; 227 fa.fa_node = node; 228 fa.fa_iot = sc->sc_iot; 229 fa.fa_dmat = sc->sc_dmat; 230 fa.fa_acells = sc->sc_acells; 231 fa.fa_scells = sc->sc_scells; 232 233 len = OF_getproplen(node, "reg"); 234 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 235 if (len > 0 && (len % line) == 0) { 236 reg = malloc(len, M_TEMP, M_WAITOK); 237 OF_getpropintarray(node, "reg", reg, len); 238 239 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 240 M_DEVBUF, M_WAITOK); 241 fa.fa_nreg = (len / line); 242 243 for (i = 0, cell = reg; i < len / line; i++) { 244 if (sc->sc_acells >= 1) 245 fa.fa_reg[i].addr = cell[0]; 246 if (sc->sc_acells == 2) { 247 fa.fa_reg[i].addr <<= 32; 248 fa.fa_reg[i].addr |= cell[1]; 249 } 250 cell += sc->sc_acells; 251 if (sc->sc_scells >= 1) 252 fa.fa_reg[i].size = cell[0]; 253 if (sc->sc_scells == 2) { 254 fa.fa_reg[i].size <<= 32; 255 fa.fa_reg[i].size |= cell[1]; 256 } 257 cell += sc->sc_scells; 258 } 259 260 free(reg, M_TEMP, len); 261 } 262 263 len = OF_getproplen(node, "interrupts"); 264 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 265 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 266 fa.fa_nintr = len / sizeof(uint32_t); 267 268 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 269 } 270 271 if (OF_getproplen(node, "dma-coherent") >= 0) { 272 fa.fa_dmat = malloc(sizeof(*sc->sc_dmat), 273 M_DEVBUF, M_WAITOK | M_ZERO); 274 memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat)); 275 fa.fa_dmat->_flags |= BUS_DMA_COHERENT; 276 } 277 278 fa.fa_dmat = iommu_device_map(fa.fa_node, fa.fa_dmat); 279 280 if (submatch == NULL && sc->sc_early == 0) 281 print = mainbus_print; 282 if (submatch == NULL) 283 submatch = mainbus_match_status; 284 285 child = config_found_sm(self, &fa, print, submatch); 286 287 /* Record nodes that we attach early. */ 288 if (child && sc->sc_early) { 289 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 290 if (sc->sc_early_nodes[i] != 0) 291 continue; 292 sc->sc_early_nodes[i] = node; 293 break; 294 } 295 } 296 297 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 298 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 299 } 300 301 int 302 mainbus_match_status(struct device *parent, void *match, void *aux) 303 { 304 struct mainbus_softc *sc = (struct mainbus_softc *)parent; 305 struct fdt_attach_args *fa = aux; 306 struct cfdata *cf = match; 307 char buf[32]; 308 309 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 310 strcmp(buf, "disabled") == 0) 311 return 0; 312 313 if (cf->cf_loc[0] == sc->sc_early) 314 return (*cf->cf_attach->ca_match)(parent, match, aux); 315 316 return 0; 317 } 318 319 void 320 mainbus_attach_cpus(struct device *self, cfmatch_t match) 321 { 322 struct mainbus_softc *sc = (struct mainbus_softc *)self; 323 int node = OF_finddevice("/cpus"); 324 int acells, scells; 325 char buf[32]; 326 327 if (node == -1) 328 return; 329 330 acells = sc->sc_acells; 331 scells = sc->sc_scells; 332 sc->sc_acells = OF_getpropint(node, "#address-cells", 2); 333 sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 334 335 ncpusfound = 0; 336 for (node = OF_child(node); node != 0; node = OF_peer(node)) { 337 if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 && 338 strcmp(buf, "cpu") == 0) 339 ncpusfound++; 340 341 mainbus_attach_node(self, node, match); 342 } 343 344 sc->sc_acells = acells; 345 sc->sc_scells = scells; 346 } 347 348 int 349 mainbus_match_primary(struct device *parent, void *match, void *aux) 350 { 351 struct fdt_attach_args *fa = aux; 352 struct cfdata *cf = match; 353 uint64_t mpidr = READ_SPECIALREG(mpidr_el1); 354 355 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF)) 356 return 0; 357 358 return (*cf->cf_attach->ca_match)(parent, match, aux); 359 } 360 361 int 362 mainbus_match_secondary(struct device *parent, void *match, void *aux) 363 { 364 struct fdt_attach_args *fa = aux; 365 struct cfdata *cf = match; 366 uint64_t mpidr = READ_SPECIALREG(mpidr_el1); 367 368 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF)) 369 return 0; 370 371 return (*cf->cf_attach->ca_match)(parent, match, aux); 372 } 373 374 void 375 mainbus_attach_psci(struct device *self) 376 { 377 struct mainbus_softc *sc = (struct mainbus_softc *)self; 378 int node = OF_finddevice("/psci"); 379 380 if (node == -1) 381 return; 382 383 sc->sc_early = 1; 384 mainbus_attach_node(self, node, NULL); 385 sc->sc_early = 0; 386 } 387 388 void 389 mainbus_attach_efi(struct device *self) 390 { 391 struct mainbus_softc *sc = (struct mainbus_softc *)self; 392 struct fdt_attach_args fa; 393 int node = OF_finddevice("/chosen"); 394 395 if (node == -1 || 396 OF_getproplen(node, "openbsd,uefi-system-table") <= 0) 397 return; 398 399 memset(&fa, 0, sizeof(fa)); 400 fa.fa_name = "efi"; 401 fa.fa_iot = sc->sc_iot; 402 fa.fa_dmat = sc->sc_dmat; 403 config_found(self, &fa, NULL); 404 } 405 406 void 407 mainbus_attach_apm(struct device *self) 408 { 409 struct fdt_attach_args fa; 410 411 memset(&fa, 0, sizeof(fa)); 412 fa.fa_name = "apm"; 413 414 config_found(self, &fa, NULL); 415 } 416 417 void 418 mainbus_attach_framebuffer(struct device *self) 419 { 420 int node = OF_finddevice("/chosen"); 421 422 if (node == -1) 423 return; 424 425 for (node = OF_child(node); node != 0; node = OF_peer(node)) 426 mainbus_attach_node(self, node, NULL); 427 } 428 429 void 430 mainbus_attach_firmware(struct device *self) 431 { 432 int node = OF_finddevice("/firmware"); 433 434 if (node == -1) 435 return; 436 437 for (node = OF_child(node); node != 0; node = OF_peer(node)) 438 mainbus_attach_node(self, node, NULL); 439 } 440 441 void 442 mainbus_attach_resvmem(struct device *self) 443 { 444 int node = OF_finddevice("/reserved-memory"); 445 446 if (node == -1) 447 return; 448 449 for (node = OF_child(node); node != 0; node = OF_peer(node)) 450 mainbus_attach_node(self, node, NULL); 451 } 452