1*130ea1ecSderaadt /* $OpenBSD: mainbus.c,v 1.7 2024/08/18 15:50:49 deraadt Exp $ */ 28c07258aSkettenis /* 38c07258aSkettenis * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se> 48c07258aSkettenis * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 58c07258aSkettenis * 68c07258aSkettenis * Permission to use, copy, modify, and distribute this software for any 78c07258aSkettenis * purpose with or without fee is hereby granted, provided that the above 88c07258aSkettenis * copyright notice and this permission notice appear in all copies. 98c07258aSkettenis * 108c07258aSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 118c07258aSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 128c07258aSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 138c07258aSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 148c07258aSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 158c07258aSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 168c07258aSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 178c07258aSkettenis */ 188c07258aSkettenis 198c07258aSkettenis #include <sys/param.h> 208c07258aSkettenis #include <sys/systm.h> 218c07258aSkettenis #include <sys/kernel.h> 228c07258aSkettenis #include <sys/device.h> 238c07258aSkettenis #include <sys/malloc.h> 248c07258aSkettenis 258c07258aSkettenis #include <machine/cpufunc.h> 268c07258aSkettenis #include <machine/fdt.h> 278c07258aSkettenis #include <dev/ofw/openfirm.h> 288c07258aSkettenis #include <dev/ofw/fdt.h> 298c07258aSkettenis #include <dev/ofw/ofw_thermal.h> 308c07258aSkettenis 318c07258aSkettenis int mainbus_match(struct device *, void *, void *); 328c07258aSkettenis void mainbus_attach(struct device *, struct device *, void *); 338c07258aSkettenis 348c07258aSkettenis void mainbus_attach_node(struct device *, int, cfmatch_t); 358c07258aSkettenis int mainbus_match_status(struct device *, void *, void *); 368c07258aSkettenis void mainbus_attach_cpus(struct device *, cfmatch_t); 378c07258aSkettenis int mainbus_match_primary(struct device *, void *, void *); 388c07258aSkettenis int mainbus_match_secondary(struct device *, void *, void *); 398c07258aSkettenis 408c07258aSkettenis struct mainbus_softc { 418c07258aSkettenis struct device sc_dev; 428c07258aSkettenis int sc_node; 438c07258aSkettenis bus_space_tag_t sc_iot; 448c07258aSkettenis bus_dma_tag_t sc_dmat; 458c07258aSkettenis int sc_acells; 468c07258aSkettenis int sc_scells; 478c07258aSkettenis int *sc_ranges; 488c07258aSkettenis int sc_rangeslen; 498c07258aSkettenis int sc_early; 508c07258aSkettenis int sc_early_nodes[64]; 518c07258aSkettenis }; 528c07258aSkettenis 53471aeecfSnaddy const struct cfattach mainbus_ca = { 54*130ea1ecSderaadt sizeof(struct mainbus_softc), mainbus_match, mainbus_attach 558c07258aSkettenis }; 568c07258aSkettenis 578c07258aSkettenis struct cfdriver mainbus_cd = { 588c07258aSkettenis NULL, "mainbus", DV_DULL 598c07258aSkettenis }; 608c07258aSkettenis 61352f7f3bSkettenis struct bus_space mainbus_bus_space = { 62352f7f3bSkettenis ._space_read_1 = generic_space_read_1, 63352f7f3bSkettenis ._space_write_1 = generic_space_write_1, 64352f7f3bSkettenis ._space_read_2 = generic_space_read_2, 65352f7f3bSkettenis ._space_write_2 = generic_space_write_2, 66352f7f3bSkettenis ._space_read_4 = generic_space_read_4, 67352f7f3bSkettenis ._space_write_4 = generic_space_write_4, 68352f7f3bSkettenis ._space_read_8 = generic_space_read_8, 69352f7f3bSkettenis ._space_write_8 = generic_space_write_8, 70352f7f3bSkettenis ._space_read_raw_2 = generic_space_read_raw_2, 71352f7f3bSkettenis ._space_write_raw_2 = generic_space_write_raw_2, 72352f7f3bSkettenis ._space_read_raw_4 = generic_space_read_raw_4, 73352f7f3bSkettenis ._space_write_raw_4 = generic_space_write_raw_4, 74352f7f3bSkettenis ._space_read_raw_8 = generic_space_read_raw_8, 75352f7f3bSkettenis ._space_write_raw_8 = generic_space_write_raw_8, 76352f7f3bSkettenis ._space_map = generic_space_map, 77352f7f3bSkettenis ._space_unmap = generic_space_unmap, 78352f7f3bSkettenis ._space_subregion = generic_space_region, 79352f7f3bSkettenis ._space_vaddr = generic_space_vaddr, 80352f7f3bSkettenis ._space_mmap = generic_space_mmap 81352f7f3bSkettenis }; 82352f7f3bSkettenis 838c07258aSkettenis struct machine_bus_dma_tag mainbus_dma_tag = { 848c07258aSkettenis NULL, 858c07258aSkettenis 0, 868c07258aSkettenis _dmamap_create, 878c07258aSkettenis _dmamap_destroy, 888c07258aSkettenis _dmamap_load, 898c07258aSkettenis _dmamap_load_mbuf, 908c07258aSkettenis _dmamap_load_uio, 918c07258aSkettenis _dmamap_load_raw, 928c07258aSkettenis _dmamap_load_buffer, 938c07258aSkettenis _dmamap_unload, 948c07258aSkettenis _dmamap_sync, 958c07258aSkettenis _dmamem_alloc, 968c07258aSkettenis _dmamem_free, 978c07258aSkettenis _dmamem_map, 988c07258aSkettenis _dmamem_unmap, 998c07258aSkettenis _dmamem_mmap, 1008c07258aSkettenis }; 1018c07258aSkettenis 1028c07258aSkettenis /* 1038c07258aSkettenis * Mainbus takes care of FDT and non-FDT machines, so we 1048c07258aSkettenis * always attach. 1058c07258aSkettenis */ 1068c07258aSkettenis int 1078c07258aSkettenis mainbus_match(struct device *parent, void *cfdata, void *aux) 1088c07258aSkettenis { 1098c07258aSkettenis return (1); 1108c07258aSkettenis } 1118c07258aSkettenis 1128c07258aSkettenis void 1138c07258aSkettenis mainbus_attach(struct device *parent, struct device *self, void *aux) 1148c07258aSkettenis { 1158c07258aSkettenis struct mainbus_softc *sc = (struct mainbus_softc *)self; 1168c07258aSkettenis char prop[128]; 1178c07258aSkettenis int node, len; 1188c07258aSkettenis 1198c07258aSkettenis sc->sc_node = OF_peer(0); 120352f7f3bSkettenis sc->sc_iot = &mainbus_bus_space; 121184c283eSkettenis sc->sc_dmat = &mainbus_dma_tag; 1228c07258aSkettenis sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 1238c07258aSkettenis sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 1248c07258aSkettenis 1258c07258aSkettenis len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop)); 1268c07258aSkettenis if (len > 0) { 1278c07258aSkettenis printf(": %s\n", prop); 1288c07258aSkettenis hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 1298c07258aSkettenis if (hw_prod) 1308c07258aSkettenis strlcpy(hw_prod, prop, len); 1318c07258aSkettenis } else 1328c07258aSkettenis printf(": unknown model\n"); 1338c07258aSkettenis 1348c07258aSkettenis len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop)); 1358c07258aSkettenis if (len > 0) { 1368c07258aSkettenis hw_serial = malloc(len, M_DEVBUF, M_NOWAIT); 1378c07258aSkettenis if (hw_serial) 1388c07258aSkettenis strlcpy(hw_serial, prop, len); 1398c07258aSkettenis } 1408c07258aSkettenis 1418c07258aSkettenis /* Attach primary CPU first. */ 1428c07258aSkettenis mainbus_attach_cpus(self, mainbus_match_primary); 1438c07258aSkettenis 144612a5197Skettenis /* Attach secondary CPUs. */ 145612a5197Skettenis mainbus_attach_cpus(self, mainbus_match_secondary); 146612a5197Skettenis 1478c07258aSkettenis sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 1488c07258aSkettenis if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 1498c07258aSkettenis sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 1508c07258aSkettenis OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 1518c07258aSkettenis sc->sc_rangeslen); 1528c07258aSkettenis } 1538c07258aSkettenis 1548c07258aSkettenis /* Scan the whole tree. */ 1558c07258aSkettenis sc->sc_early = 1; 1568c07258aSkettenis for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 1578c07258aSkettenis mainbus_attach_node(self, node, NULL); 1588c07258aSkettenis 1598c07258aSkettenis sc->sc_early = 0; 1608c07258aSkettenis for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 1618c07258aSkettenis mainbus_attach_node(self, node, NULL); 1628c07258aSkettenis } 1638c07258aSkettenis 1648c07258aSkettenis int 1658c07258aSkettenis mainbus_print(void *aux, const char *pnp) 1668c07258aSkettenis { 1678c07258aSkettenis struct fdt_attach_args *fa = aux; 1688c07258aSkettenis char buf[32]; 1698c07258aSkettenis 1708c07258aSkettenis if (!pnp) 1718c07258aSkettenis return (QUIET); 1728c07258aSkettenis 1738c07258aSkettenis if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 1748c07258aSkettenis strcmp(buf, "disabled") == 0) 1758c07258aSkettenis return (QUIET); 1768c07258aSkettenis 1778c07258aSkettenis if (OF_getprop(fa->fa_node, "name", buf, sizeof(buf)) > 0) { 1788c07258aSkettenis buf[sizeof(buf) - 1] = 0; 1798c07258aSkettenis if (strcmp(buf, "aliases") == 0 || 1808c07258aSkettenis strcmp(buf, "chosen") == 0 || 1818c07258aSkettenis strcmp(buf, "cpus") == 0 || 1828c07258aSkettenis strcmp(buf, "memory") == 0 || 1838c07258aSkettenis strcmp(buf, "reserved-memory") == 0 || 1848c07258aSkettenis strcmp(buf, "thermal-zones") == 0 || 1858c07258aSkettenis strncmp(buf, "__", 2) == 0) 1868c07258aSkettenis return (QUIET); 1878c07258aSkettenis printf("\"%s\"", buf); 1888c07258aSkettenis } else 1898c07258aSkettenis printf("node %u", fa->fa_node); 1908c07258aSkettenis 1918c07258aSkettenis printf(" at %s", pnp); 1928c07258aSkettenis 1938c07258aSkettenis return (UNCONF); 1948c07258aSkettenis } 1958c07258aSkettenis 1968c07258aSkettenis /* 1978c07258aSkettenis * Look for a driver that wants to be attached to this node. 1988c07258aSkettenis */ 1998c07258aSkettenis void 2008c07258aSkettenis mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 2018c07258aSkettenis { 2028c07258aSkettenis struct mainbus_softc *sc = (struct mainbus_softc *)self; 2038c07258aSkettenis struct fdt_attach_args fa; 2048c07258aSkettenis int i, len, line; 2058c07258aSkettenis uint32_t *cell, *reg; 2068c07258aSkettenis struct device *child; 2078c07258aSkettenis cfprint_t print = NULL; 2088c07258aSkettenis 2098c07258aSkettenis /* Skip if already attached early. */ 2108c07258aSkettenis for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 2118c07258aSkettenis if (sc->sc_early_nodes[i] == node) 2128c07258aSkettenis return; 2138c07258aSkettenis if (sc->sc_early_nodes[i] == 0) 2148c07258aSkettenis break; 2158c07258aSkettenis } 2168c07258aSkettenis 2178c07258aSkettenis memset(&fa, 0, sizeof(fa)); 2188c07258aSkettenis fa.fa_name = ""; 2198c07258aSkettenis fa.fa_node = node; 2208c07258aSkettenis fa.fa_iot = sc->sc_iot; 2218c07258aSkettenis fa.fa_dmat = sc->sc_dmat; 2228c07258aSkettenis fa.fa_acells = sc->sc_acells; 2238c07258aSkettenis fa.fa_scells = sc->sc_scells; 2248c07258aSkettenis 2258c07258aSkettenis len = OF_getproplen(node, "reg"); 2268c07258aSkettenis line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 2278c07258aSkettenis if (len > 0 && (len % line) == 0) { 2288c07258aSkettenis reg = malloc(len, M_TEMP, M_WAITOK); 2298c07258aSkettenis OF_getpropintarray(node, "reg", reg, len); 2308c07258aSkettenis 2318c07258aSkettenis fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 2328c07258aSkettenis M_DEVBUF, M_WAITOK); 2338c07258aSkettenis fa.fa_nreg = (len / line); 2348c07258aSkettenis 2358c07258aSkettenis for (i = 0, cell = reg; i < len / line; i++) { 2368c07258aSkettenis if (sc->sc_acells >= 1) 2378c07258aSkettenis fa.fa_reg[i].addr = cell[0]; 2388c07258aSkettenis if (sc->sc_acells == 2) { 2398c07258aSkettenis fa.fa_reg[i].addr <<= 32; 2408c07258aSkettenis fa.fa_reg[i].addr |= cell[1]; 2418c07258aSkettenis } 2428c07258aSkettenis cell += sc->sc_acells; 2438c07258aSkettenis if (sc->sc_scells >= 1) 2448c07258aSkettenis fa.fa_reg[i].size = cell[0]; 2458c07258aSkettenis if (sc->sc_scells == 2) { 2468c07258aSkettenis fa.fa_reg[i].size <<= 32; 2478c07258aSkettenis fa.fa_reg[i].size |= cell[1]; 2488c07258aSkettenis } 2498c07258aSkettenis cell += sc->sc_scells; 2508c07258aSkettenis } 2518c07258aSkettenis 2528c07258aSkettenis free(reg, M_TEMP, len); 2538c07258aSkettenis } 2548c07258aSkettenis 2558c07258aSkettenis len = OF_getproplen(node, "interrupts"); 2568c07258aSkettenis if (len > 0 && (len % sizeof(uint32_t)) == 0) { 2578c07258aSkettenis fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 2588c07258aSkettenis fa.fa_nintr = len / sizeof(uint32_t); 2598c07258aSkettenis 2608c07258aSkettenis OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 2618c07258aSkettenis } 2628c07258aSkettenis 2638c07258aSkettenis #ifdef notyet 2648c07258aSkettenis if (OF_getproplen(node, "dma-coherent") >= 0) { 2658c07258aSkettenis fa.fa_dmat = malloc(sizeof(*sc->sc_dmat), 2668c07258aSkettenis M_DEVBUF, M_WAITOK | M_ZERO); 2678c07258aSkettenis memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat)); 2688c07258aSkettenis fa.fa_dmat->_flags |= BUS_DMA_COHERENT; 2698c07258aSkettenis } 2708c07258aSkettenis #endif 2718c07258aSkettenis 2728c07258aSkettenis if (submatch == NULL && sc->sc_early == 0) 2738c07258aSkettenis print = mainbus_print; 2748c07258aSkettenis if (submatch == NULL) 2758c07258aSkettenis submatch = mainbus_match_status; 2768c07258aSkettenis 2778c07258aSkettenis child = config_found_sm(self, &fa, print, submatch); 2788c07258aSkettenis 2798c07258aSkettenis /* Record nodes that we attach early. */ 2808c07258aSkettenis if (child && sc->sc_early) { 2818c07258aSkettenis for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 2828c07258aSkettenis if (sc->sc_early_nodes[i] != 0) 2838c07258aSkettenis continue; 2848c07258aSkettenis sc->sc_early_nodes[i] = node; 2858c07258aSkettenis break; 2868c07258aSkettenis } 2878c07258aSkettenis } 2888c07258aSkettenis 2898c07258aSkettenis free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 2908c07258aSkettenis free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 2918c07258aSkettenis } 2928c07258aSkettenis 2938c07258aSkettenis int 2948c07258aSkettenis mainbus_match_status(struct device *parent, void *match, void *aux) 2958c07258aSkettenis { 2968c07258aSkettenis struct mainbus_softc *sc = (struct mainbus_softc *)parent; 2978c07258aSkettenis struct fdt_attach_args *fa = aux; 2988c07258aSkettenis struct cfdata *cf = match; 2998c07258aSkettenis char buf[32]; 3008c07258aSkettenis 3018c07258aSkettenis if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 3028c07258aSkettenis strcmp(buf, "disabled") == 0) 3038c07258aSkettenis return 0; 3048c07258aSkettenis 3058c07258aSkettenis if (cf->cf_loc[0] == sc->sc_early) 3068c07258aSkettenis return (*cf->cf_attach->ca_match)(parent, match, aux); 3078c07258aSkettenis 3088c07258aSkettenis return 0; 3098c07258aSkettenis } 3108c07258aSkettenis 3118c07258aSkettenis void 3128c07258aSkettenis mainbus_attach_cpus(struct device *self, cfmatch_t match) 3138c07258aSkettenis { 3148c07258aSkettenis struct mainbus_softc *sc = (struct mainbus_softc *)self; 3158c07258aSkettenis int node = OF_finddevice("/cpus"); 3168c07258aSkettenis int acells, scells; 3178c07258aSkettenis char buf[32]; 3188c07258aSkettenis 3198c07258aSkettenis if (node == 0) 3208c07258aSkettenis return; 3218c07258aSkettenis 3228c07258aSkettenis acells = sc->sc_acells; 3238c07258aSkettenis scells = sc->sc_scells; 3248c07258aSkettenis sc->sc_acells = OF_getpropint(node, "#address-cells", 1); 3258c07258aSkettenis sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 3268c07258aSkettenis 3278c07258aSkettenis ncpusfound = 0; 3288c07258aSkettenis for (node = OF_child(node); node != 0; node = OF_peer(node)) { 3298c07258aSkettenis if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 && 3308c07258aSkettenis strcmp(buf, "cpu") == 0) 3318c07258aSkettenis ncpusfound++; 3328c07258aSkettenis 3338c07258aSkettenis mainbus_attach_node(self, node, match); 3348c07258aSkettenis } 3358c07258aSkettenis 3368c07258aSkettenis sc->sc_acells = acells; 3378c07258aSkettenis sc->sc_scells = scells; 3388c07258aSkettenis } 3398c07258aSkettenis 3408c07258aSkettenis int 3418c07258aSkettenis mainbus_match_primary(struct device *parent, void *match, void *aux) 3428c07258aSkettenis { 3438c07258aSkettenis struct fdt_attach_args *fa = aux; 3448c07258aSkettenis struct cfdata *cf = match; 3458c07258aSkettenis 3468c07258aSkettenis if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != mfpir()) 3478c07258aSkettenis return 0; 3488c07258aSkettenis 3498c07258aSkettenis return (*cf->cf_attach->ca_match)(parent, match, aux); 3508c07258aSkettenis } 3518c07258aSkettenis 3528c07258aSkettenis int 3538c07258aSkettenis mainbus_match_secondary(struct device *parent, void *match, void *aux) 3548c07258aSkettenis { 3558c07258aSkettenis struct fdt_attach_args *fa = aux; 3568c07258aSkettenis struct cfdata *cf = match; 3578c07258aSkettenis 3588c07258aSkettenis if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == mfpir()) 3598c07258aSkettenis return 0; 3608c07258aSkettenis 3618c07258aSkettenis return (*cf->cf_attach->ca_match)(parent, match, aux); 3628c07258aSkettenis } 363