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