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