1 /* $OpenBSD: mainbus.c,v 1.22 2021/10/24 17:52:28 mpi 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 47 struct mainbus_softc { 48 struct device sc_dev; 49 int sc_node; 50 bus_space_tag_t sc_iot; 51 bus_dma_tag_t sc_dmat; 52 int sc_acells; 53 int sc_scells; 54 int *sc_ranges; 55 int sc_rangeslen; 56 int sc_early; 57 int sc_early_nodes[64]; 58 }; 59 60 const struct cfattach mainbus_ca = { 61 sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL, 62 config_activate_children 63 }; 64 65 struct cfdriver mainbus_cd = { 66 NULL, "mainbus", DV_DULL 67 }; 68 69 struct machine_bus_dma_tag mainbus_dma_tag = { 70 NULL, 71 0, 72 _dmamap_create, 73 _dmamap_destroy, 74 _dmamap_load, 75 _dmamap_load_mbuf, 76 _dmamap_load_uio, 77 _dmamap_load_raw, 78 _dmamap_load_buffer, 79 _dmamap_unload, 80 _dmamap_sync, 81 _dmamem_alloc, 82 _dmamem_free, 83 _dmamem_map, 84 _dmamem_unmap, 85 _dmamem_mmap, 86 }; 87 88 /* 89 * Mainbus takes care of FDT and non-FDT machines, so we 90 * always attach. 91 */ 92 int 93 mainbus_match(struct device *parent, void *cfdata, void *aux) 94 { 95 return (1); 96 } 97 98 void agtimer_init(void); 99 100 void 101 mainbus_attach(struct device *parent, struct device *self, void *aux) 102 { 103 struct mainbus_softc *sc = (struct mainbus_softc *)self; 104 char prop[128]; 105 int node, len; 106 107 arm_intr_init_fdt(); 108 agtimer_init(); 109 110 sc->sc_node = OF_peer(0); 111 sc->sc_iot = &arm64_bs_tag; 112 sc->sc_dmat = &mainbus_dma_tag; 113 sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 114 sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 115 116 len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop)); 117 if (len > 0) { 118 printf(": %s\n", prop); 119 hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 120 if (hw_prod) 121 strlcpy(hw_prod, prop, len); 122 } else 123 printf(": unknown model\n"); 124 125 len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop)); 126 if (len > 0) { 127 hw_serial = malloc(len, M_DEVBUF, M_NOWAIT); 128 if (hw_serial) 129 strlcpy(hw_serial, prop, len); 130 } 131 132 mainbus_attach_psci(self); 133 134 /* Attach primary CPU first. */ 135 mainbus_attach_cpus(self, mainbus_match_primary); 136 137 /* Attach secondary CPUs. */ 138 mainbus_attach_cpus(self, mainbus_match_secondary); 139 140 mainbus_attach_efi(self); 141 142 sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 143 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 144 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 145 OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 146 sc->sc_rangeslen); 147 } 148 149 mainbus_attach_apm(self); 150 151 /* Scan the whole tree. */ 152 sc->sc_early = 1; 153 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 154 mainbus_attach_node(self, node, NULL); 155 156 sc->sc_early = 0; 157 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 158 mainbus_attach_node(self, node, NULL); 159 160 mainbus_attach_framebuffer(self); 161 162 thermal_init(); 163 } 164 165 int 166 mainbus_print(void *aux, const char *pnp) 167 { 168 struct fdt_attach_args *fa = aux; 169 char buf[32]; 170 171 if (!pnp) 172 return (QUIET); 173 174 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 175 strcmp(buf, "disabled") == 0) 176 return (QUIET); 177 178 if (OF_getprop(fa->fa_node, "name", buf, sizeof(buf)) > 0) { 179 buf[sizeof(buf) - 1] = 0; 180 if (strcmp(buf, "aliases") == 0 || 181 strcmp(buf, "chosen") == 0 || 182 strcmp(buf, "cpus") == 0 || 183 strcmp(buf, "memory") == 0 || 184 strcmp(buf, "reserved-memory") == 0 || 185 strcmp(buf, "thermal-zones") == 0 || 186 strncmp(buf, "__", 2) == 0) 187 return (QUIET); 188 printf("\"%s\"", buf); 189 } else 190 printf("node %u", fa->fa_node); 191 192 printf(" at %s", pnp); 193 194 return (UNCONF); 195 } 196 197 /* 198 * Look for a driver that wants to be attached to this node. 199 */ 200 void 201 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 202 { 203 struct mainbus_softc *sc = (struct mainbus_softc *)self; 204 struct fdt_attach_args fa; 205 int i, len, line; 206 uint32_t *cell, *reg; 207 struct device *child; 208 cfprint_t print = NULL; 209 210 /* Skip if already attached early. */ 211 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 212 if (sc->sc_early_nodes[i] == node) 213 return; 214 if (sc->sc_early_nodes[i] == 0) 215 break; 216 } 217 218 memset(&fa, 0, sizeof(fa)); 219 fa.fa_name = ""; 220 fa.fa_node = node; 221 fa.fa_iot = sc->sc_iot; 222 fa.fa_dmat = sc->sc_dmat; 223 fa.fa_acells = sc->sc_acells; 224 fa.fa_scells = sc->sc_scells; 225 226 len = OF_getproplen(node, "reg"); 227 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 228 if (len > 0 && (len % line) == 0) { 229 reg = malloc(len, M_TEMP, M_WAITOK); 230 OF_getpropintarray(node, "reg", reg, len); 231 232 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 233 M_DEVBUF, M_WAITOK); 234 fa.fa_nreg = (len / line); 235 236 for (i = 0, cell = reg; i < len / line; i++) { 237 if (sc->sc_acells >= 1) 238 fa.fa_reg[i].addr = cell[0]; 239 if (sc->sc_acells == 2) { 240 fa.fa_reg[i].addr <<= 32; 241 fa.fa_reg[i].addr |= cell[1]; 242 } 243 cell += sc->sc_acells; 244 if (sc->sc_scells >= 1) 245 fa.fa_reg[i].size = cell[0]; 246 if (sc->sc_scells == 2) { 247 fa.fa_reg[i].size <<= 32; 248 fa.fa_reg[i].size |= cell[1]; 249 } 250 cell += sc->sc_scells; 251 } 252 253 free(reg, M_TEMP, len); 254 } 255 256 len = OF_getproplen(node, "interrupts"); 257 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 258 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 259 fa.fa_nintr = len / sizeof(uint32_t); 260 261 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 262 } 263 264 if (OF_getproplen(node, "dma-coherent") >= 0) { 265 fa.fa_dmat = malloc(sizeof(*sc->sc_dmat), 266 M_DEVBUF, M_WAITOK | M_ZERO); 267 memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat)); 268 fa.fa_dmat->_flags |= BUS_DMA_COHERENT; 269 } 270 271 fa.fa_dmat = iommu_device_map(fa.fa_node, fa.fa_dmat); 272 273 if (submatch == NULL && sc->sc_early == 0) 274 print = mainbus_print; 275 if (submatch == NULL) 276 submatch = mainbus_match_status; 277 278 child = config_found_sm(self, &fa, print, submatch); 279 280 /* Record nodes that we attach early. */ 281 if (child && sc->sc_early) { 282 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 283 if (sc->sc_early_nodes[i] != 0) 284 continue; 285 sc->sc_early_nodes[i] = node; 286 break; 287 } 288 } 289 290 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 291 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 292 } 293 294 int 295 mainbus_match_status(struct device *parent, void *match, void *aux) 296 { 297 struct mainbus_softc *sc = (struct mainbus_softc *)parent; 298 struct fdt_attach_args *fa = aux; 299 struct cfdata *cf = match; 300 char buf[32]; 301 302 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 303 strcmp(buf, "disabled") == 0) 304 return 0; 305 306 if (cf->cf_loc[0] == sc->sc_early) 307 return (*cf->cf_attach->ca_match)(parent, match, aux); 308 309 return 0; 310 } 311 312 void 313 mainbus_attach_cpus(struct device *self, cfmatch_t match) 314 { 315 struct mainbus_softc *sc = (struct mainbus_softc *)self; 316 int node = OF_finddevice("/cpus"); 317 int acells, scells; 318 char buf[32]; 319 320 if (node == -1) 321 return; 322 323 acells = sc->sc_acells; 324 scells = sc->sc_scells; 325 sc->sc_acells = OF_getpropint(node, "#address-cells", 2); 326 sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 327 328 ncpusfound = 0; 329 for (node = OF_child(node); node != 0; node = OF_peer(node)) { 330 if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 && 331 strcmp(buf, "cpu") == 0) 332 ncpusfound++; 333 334 mainbus_attach_node(self, node, match); 335 } 336 337 sc->sc_acells = acells; 338 sc->sc_scells = scells; 339 } 340 341 int 342 mainbus_match_primary(struct device *parent, void *match, void *aux) 343 { 344 struct fdt_attach_args *fa = aux; 345 struct cfdata *cf = match; 346 uint64_t mpidr = READ_SPECIALREG(mpidr_el1); 347 348 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF)) 349 return 0; 350 351 return (*cf->cf_attach->ca_match)(parent, match, aux); 352 } 353 354 int 355 mainbus_match_secondary(struct device *parent, void *match, void *aux) 356 { 357 struct fdt_attach_args *fa = aux; 358 struct cfdata *cf = match; 359 uint64_t mpidr = READ_SPECIALREG(mpidr_el1); 360 361 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF)) 362 return 0; 363 364 return (*cf->cf_attach->ca_match)(parent, match, aux); 365 } 366 367 void 368 mainbus_attach_psci(struct device *self) 369 { 370 struct mainbus_softc *sc = (struct mainbus_softc *)self; 371 int node = OF_finddevice("/psci"); 372 373 if (node == -1) 374 return; 375 376 sc->sc_early = 1; 377 mainbus_attach_node(self, node, NULL); 378 sc->sc_early = 0; 379 } 380 381 void 382 mainbus_attach_efi(struct device *self) 383 { 384 struct mainbus_softc *sc = (struct mainbus_softc *)self; 385 struct fdt_attach_args fa; 386 int node = OF_finddevice("/chosen"); 387 388 if (node == -1 || 389 OF_getproplen(node, "openbsd,uefi-system-table") <= 0) 390 return; 391 392 memset(&fa, 0, sizeof(fa)); 393 fa.fa_name = "efi"; 394 fa.fa_iot = sc->sc_iot; 395 fa.fa_dmat = sc->sc_dmat; 396 config_found(self, &fa, NULL); 397 } 398 399 void 400 mainbus_attach_apm(struct device *self) 401 { 402 struct fdt_attach_args fa; 403 404 memset(&fa, 0, sizeof(fa)); 405 fa.fa_name = "apm"; 406 407 config_found(self, &fa, NULL); 408 } 409 410 void 411 mainbus_attach_framebuffer(struct device *self) 412 { 413 int node = OF_finddevice("/chosen"); 414 415 if (node == -1) 416 return; 417 418 for (node = OF_child(node); node != 0; node = OF_peer(node)) 419 mainbus_attach_node(self, node, NULL); 420 } 421