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