1*130ea1ecSderaadt /* $OpenBSD: mainbus.c,v 1.25 2024/08/18 15:50:49 deraadt Exp $ */ 2e1e4f5b1Sdrahn /* 379645871Spatrick * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se> 488a615dbSkettenis * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 5e1e4f5b1Sdrahn * 679645871Spatrick * Permission to use, copy, modify, and distribute this software for any 779645871Spatrick * purpose with or without fee is hereby granted, provided that the above 879645871Spatrick * copyright notice and this permission notice appear in all copies. 9e1e4f5b1Sdrahn * 1079645871Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1179645871Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1279645871Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1379645871Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1479645871Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1579645871Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1679645871Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17e1e4f5b1Sdrahn */ 18e1e4f5b1Sdrahn 19e1e4f5b1Sdrahn #include <sys/param.h> 20e1e4f5b1Sdrahn #include <sys/systm.h> 21e1e4f5b1Sdrahn #include <sys/kernel.h> 22e1e4f5b1Sdrahn #include <sys/device.h> 233dfd0072Sjsg #include <sys/malloc.h> 24e1e4f5b1Sdrahn 2579645871Spatrick #include <dev/ofw/openfirm.h> 26df9226ecSjsg #include <dev/ofw/fdt.h> 2720584b0dSkettenis #include <dev/ofw/ofw_thermal.h> 2879645871Spatrick 29e1e4f5b1Sdrahn #include <arm/mainbus/mainbus.h> 30e1e4f5b1Sdrahn 3179645871Spatrick int mainbus_match(struct device *, void *, void *); 3279645871Spatrick void mainbus_attach(struct device *, struct device *, void *); 33e1e4f5b1Sdrahn 3488a615dbSkettenis void mainbus_attach_node(struct device *, int, cfmatch_t); 3588a615dbSkettenis int mainbus_match_status(struct device *, void *, void *); 3688a615dbSkettenis void mainbus_attach_cpus(struct device *, cfmatch_t); 3788a615dbSkettenis int mainbus_match_primary(struct device *, void *, void *); 3888a615dbSkettenis int mainbus_match_secondary(struct device *, void *, void *); 396fd46d03Skettenis void mainbus_attach_framebuffer(struct device *); 40e1e4f5b1Sdrahn 4179645871Spatrick struct mainbus_softc { 4279645871Spatrick struct device sc_dev; 43b70d681bSkettenis int sc_node; 4479645871Spatrick bus_space_tag_t sc_iot; 4579645871Spatrick bus_dma_tag_t sc_dmat; 465586125eSpatrick int sc_acells; 475586125eSpatrick int sc_scells; 48df9226ecSjsg int *sc_ranges; 49df9226ecSjsg int sc_rangeslen; 50b70d681bSkettenis int sc_early; 5179645871Spatrick }; 52e1e4f5b1Sdrahn 53e3ee5e84Smpi const struct cfattach mainbus_ca = { 54*130ea1ecSderaadt sizeof(struct mainbus_softc), mainbus_match, mainbus_attach 55e1e4f5b1Sdrahn }; 56e1e4f5b1Sdrahn 57e1e4f5b1Sdrahn struct cfdriver mainbus_cd = { 58e1e4f5b1Sdrahn NULL, "mainbus", DV_DULL 59e1e4f5b1Sdrahn }; 60e1e4f5b1Sdrahn 6179645871Spatrick struct arm32_bus_dma_tag mainbus_dma_tag = { 6279645871Spatrick NULL, 6379645871Spatrick _bus_dmamap_create, 6479645871Spatrick _bus_dmamap_destroy, 6579645871Spatrick _bus_dmamap_load, 6679645871Spatrick _bus_dmamap_load_mbuf, 6779645871Spatrick _bus_dmamap_load_uio, 6879645871Spatrick _bus_dmamap_load_raw, 6937084e1eSkettenis _bus_dmamap_load_buffer, 7079645871Spatrick _bus_dmamap_unload, 7179645871Spatrick _bus_dmamap_sync, 7279645871Spatrick _bus_dmamem_alloc, 7379645871Spatrick _bus_dmamem_free, 7479645871Spatrick _bus_dmamem_map, 7579645871Spatrick _bus_dmamem_unmap, 7679645871Spatrick _bus_dmamem_mmap, 7779645871Spatrick }; 78e1e4f5b1Sdrahn 7979645871Spatrick /* 8079645871Spatrick * Mainbus takes care of FDT and non-FDT machines, so we 8179645871Spatrick * always attach. 8279645871Spatrick */ 83e1e4f5b1Sdrahn int 8479645871Spatrick mainbus_match(struct device *parent, void *cfdata, void *aux) 85e1e4f5b1Sdrahn { 86e1e4f5b1Sdrahn return (1); 87e1e4f5b1Sdrahn } 88e1e4f5b1Sdrahn 8904e4a795Sjsg extern struct bus_space armv7_bs_tag; 9004e4a795Sjsg void platform_init_mainbus(struct device *); 913dfd0072Sjsg 92e1e4f5b1Sdrahn void 9379645871Spatrick mainbus_attach(struct device *parent, struct device *self, void *aux) 94e1e4f5b1Sdrahn { 9579645871Spatrick struct mainbus_softc *sc = (struct mainbus_softc *)self; 96aaf7ae02Spatrick char prop[128]; 973dfd0072Sjsg int node, len; 98e1e4f5b1Sdrahn 993a2a51e5Skettenis arm_intr_init_fdt(); 1003a2a51e5Skettenis 101b70d681bSkettenis sc->sc_node = OF_peer(0); 10279645871Spatrick sc->sc_iot = &armv7_bs_tag; 10379645871Spatrick sc->sc_dmat = &mainbus_dma_tag; 1045586125eSpatrick sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 1055586125eSpatrick sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 106cc7b0317Sdrahn 107aaf7ae02Spatrick len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop)); 108b70d681bSkettenis if (len > 0) { 109aaf7ae02Spatrick printf(": %s\n", prop); 1103dfd0072Sjsg hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 1113dfd0072Sjsg if (hw_prod) 112aaf7ae02Spatrick strlcpy(hw_prod, prop, len); 1133dfd0072Sjsg } else 11479645871Spatrick printf(": unknown model\n"); 11579645871Spatrick 116aaf7ae02Spatrick len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop)); 117aaf7ae02Spatrick if (len > 0) { 118aaf7ae02Spatrick hw_serial = malloc(len, M_DEVBUF, M_NOWAIT); 119aaf7ae02Spatrick if (hw_serial) 120aaf7ae02Spatrick strlcpy(hw_serial, prop, len); 121aaf7ae02Spatrick } 122aaf7ae02Spatrick 12388a615dbSkettenis /* Attach primary CPU first. */ 12488a615dbSkettenis mainbus_attach_cpus(self, mainbus_match_primary); 12579645871Spatrick 12688a615dbSkettenis platform_init_mainbus(self); 12779645871Spatrick 128df9226ecSjsg sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 129df9226ecSjsg if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 130df9226ecSjsg sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 131df9226ecSjsg OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 132df9226ecSjsg sc->sc_rangeslen); 133df9226ecSjsg } 134df9226ecSjsg 13579645871Spatrick /* Scan the whole tree. */ 136b70d681bSkettenis sc->sc_early = 1; 137b70d681bSkettenis for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 138b70d681bSkettenis mainbus_attach_node(self, node, NULL); 139b70d681bSkettenis 140b70d681bSkettenis sc->sc_early = 0; 141b70d681bSkettenis for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 14288a615dbSkettenis mainbus_attach_node(self, node, NULL); 1436fd46d03Skettenis 1446fd46d03Skettenis mainbus_attach_framebuffer(self); 14588a615dbSkettenis 14688a615dbSkettenis /* Attach secondary CPUs. */ 14788a615dbSkettenis mainbus_attach_cpus(self, mainbus_match_secondary); 14820584b0dSkettenis 14920584b0dSkettenis thermal_init(); 15079645871Spatrick } 15179645871Spatrick 15279645871Spatrick /* 15379645871Spatrick * Look for a driver that wants to be attached to this node. 15479645871Spatrick */ 15579645871Spatrick void 15688a615dbSkettenis mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 15779645871Spatrick { 15879645871Spatrick struct mainbus_softc *sc = (struct mainbus_softc *)self; 15979645871Spatrick struct fdt_attach_args fa; 160df9226ecSjsg int i, len, line; 161df9226ecSjsg uint32_t *cell, *reg; 16279645871Spatrick 16379645871Spatrick memset(&fa, 0, sizeof(fa)); 16479645871Spatrick fa.fa_name = ""; 16579645871Spatrick fa.fa_node = node; 16679645871Spatrick fa.fa_iot = sc->sc_iot; 16779645871Spatrick fa.fa_dmat = sc->sc_dmat; 1685586125eSpatrick fa.fa_acells = sc->sc_acells; 1695586125eSpatrick fa.fa_scells = sc->sc_scells; 17079645871Spatrick 171df9226ecSjsg len = OF_getproplen(node, "reg"); 172df9226ecSjsg line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 173df9226ecSjsg if (len > 0 && (len % line) == 0) { 174df9226ecSjsg reg = malloc(len, M_TEMP, M_WAITOK); 175df9226ecSjsg OF_getpropintarray(node, "reg", reg, len); 176df9226ecSjsg 177df9226ecSjsg fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 178df9226ecSjsg M_DEVBUF, M_WAITOK); 179df9226ecSjsg fa.fa_nreg = (len / line); 180df9226ecSjsg 181df9226ecSjsg for (i = 0, cell = reg; i < len / line; i++) { 182df9226ecSjsg if (sc->sc_acells >= 1) 183df9226ecSjsg fa.fa_reg[i].addr = cell[0]; 184df9226ecSjsg if (sc->sc_acells == 2) { 185df9226ecSjsg fa.fa_reg[i].addr <<= 32; 186df9226ecSjsg fa.fa_reg[i].addr |= cell[1]; 187df9226ecSjsg } 188df9226ecSjsg cell += sc->sc_acells; 189df9226ecSjsg if (sc->sc_scells >= 1) 190df9226ecSjsg fa.fa_reg[i].size = cell[0]; 191df9226ecSjsg if (sc->sc_scells == 2) { 192df9226ecSjsg fa.fa_reg[i].size <<= 32; 193df9226ecSjsg fa.fa_reg[i].size |= cell[1]; 194df9226ecSjsg } 195df9226ecSjsg cell += sc->sc_scells; 196df9226ecSjsg } 197df9226ecSjsg 198df9226ecSjsg free(reg, M_TEMP, len); 199df9226ecSjsg } 200df9226ecSjsg 201df9226ecSjsg len = OF_getproplen(node, "interrupts"); 202df9226ecSjsg if (len > 0 && (len % sizeof(uint32_t)) == 0) { 203df9226ecSjsg fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 204df9226ecSjsg fa.fa_nintr = len / sizeof(uint32_t); 205df9226ecSjsg 206df9226ecSjsg OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 207df9226ecSjsg } 208df9226ecSjsg 20988a615dbSkettenis if (submatch == NULL) 21088a615dbSkettenis submatch = mainbus_match_status; 21188a615dbSkettenis config_found_sm(self, &fa, NULL, submatch); 212df9226ecSjsg 213df9226ecSjsg free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 214df9226ecSjsg free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 21579645871Spatrick } 21679645871Spatrick 21788a615dbSkettenis int 21888a615dbSkettenis mainbus_match_status(struct device *parent, void *match, void *aux) 21988a615dbSkettenis { 220b70d681bSkettenis struct mainbus_softc *sc = (struct mainbus_softc *)parent; 22188a615dbSkettenis struct fdt_attach_args *fa = aux; 22288a615dbSkettenis struct cfdata *cf = match; 22388a615dbSkettenis char buf[32]; 22488a615dbSkettenis 22588a615dbSkettenis if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 22688a615dbSkettenis strcmp(buf, "disabled") == 0) 22788a615dbSkettenis return 0; 22888a615dbSkettenis 229b70d681bSkettenis if (cf->cf_loc[0] == sc->sc_early) 23088a615dbSkettenis return (*cf->cf_attach->ca_match)(parent, match, aux); 231b70d681bSkettenis return 0; 23288a615dbSkettenis } 23388a615dbSkettenis 23488a615dbSkettenis void 23588a615dbSkettenis mainbus_attach_cpus(struct device *self, cfmatch_t match) 23688a615dbSkettenis { 23788a615dbSkettenis struct mainbus_softc *sc = (struct mainbus_softc *)self; 23888a615dbSkettenis int node = OF_finddevice("/cpus"); 23988a615dbSkettenis int acells, scells; 24088a615dbSkettenis 24188a615dbSkettenis if (node == 0) 24288a615dbSkettenis return; 24388a615dbSkettenis 24488a615dbSkettenis acells = sc->sc_acells; 24588a615dbSkettenis scells = sc->sc_scells; 24688a615dbSkettenis sc->sc_acells = OF_getpropint(node, "#address-cells", 1); 24788a615dbSkettenis sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 24888a615dbSkettenis 24988a615dbSkettenis for (node = OF_child(node); node != 0; node = OF_peer(node)) 25088a615dbSkettenis mainbus_attach_node(self, node, match); 25188a615dbSkettenis 25288a615dbSkettenis sc->sc_acells = acells; 25388a615dbSkettenis sc->sc_scells = scells; 25488a615dbSkettenis } 25588a615dbSkettenis 25688a615dbSkettenis int 25788a615dbSkettenis mainbus_match_primary(struct device *parent, void *match, void *aux) 25888a615dbSkettenis { 25988a615dbSkettenis struct fdt_attach_args *fa = aux; 26088a615dbSkettenis struct cfdata *cf = match; 26188a615dbSkettenis uint32_t mpidr; 26288a615dbSkettenis 26388a615dbSkettenis __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 26488a615dbSkettenis 26588a615dbSkettenis if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF)) 26688a615dbSkettenis return 0; 26788a615dbSkettenis 26888a615dbSkettenis return (*cf->cf_attach->ca_match)(parent, match, aux); 26988a615dbSkettenis } 27088a615dbSkettenis 27188a615dbSkettenis int 27288a615dbSkettenis mainbus_match_secondary(struct device *parent, void *match, void *aux) 27388a615dbSkettenis { 27488a615dbSkettenis struct fdt_attach_args *fa = aux; 27588a615dbSkettenis struct cfdata *cf = match; 27688a615dbSkettenis uint32_t mpidr; 27788a615dbSkettenis 27888a615dbSkettenis __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 27988a615dbSkettenis 28088a615dbSkettenis if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF)) 28188a615dbSkettenis return 0; 28288a615dbSkettenis 28388a615dbSkettenis return (*cf->cf_attach->ca_match)(parent, match, aux); 28488a615dbSkettenis } 28588a615dbSkettenis 2866fd46d03Skettenis void 2876fd46d03Skettenis mainbus_attach_framebuffer(struct device *self) 2886fd46d03Skettenis { 2896fd46d03Skettenis int node = OF_finddevice("/chosen"); 2906fd46d03Skettenis 2916fd46d03Skettenis if (node == 0) 2926fd46d03Skettenis return; 2936fd46d03Skettenis 2946fd46d03Skettenis for (node = OF_child(node); node != 0; node = OF_peer(node)) 29588a615dbSkettenis mainbus_attach_node(self, node, NULL); 2966fd46d03Skettenis } 2976fd46d03Skettenis 29879645871Spatrick /* 29988a615dbSkettenis * Legacy support for SoCs that do not fully use FDT. 30079645871Spatrick */ 30179645871Spatrick void 30279645871Spatrick mainbus_legacy_found(struct device *self, char *name) 303e1e4f5b1Sdrahn { 30479645871Spatrick union mainbus_attach_args ma; 305afed0fbfSmiod 30679645871Spatrick memset(&ma, 0, sizeof(ma)); 30779645871Spatrick ma.ma_name = name; 308afed0fbfSmiod 30979645871Spatrick config_found(self, &ma, NULL); 310e1e4f5b1Sdrahn } 311