1 /* $OpenBSD: mainbus.c,v 1.25 2024/08/18 15:50:49 deraadt 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 <dev/ofw/openfirm.h> 26 #include <dev/ofw/fdt.h> 27 #include <dev/ofw/ofw_thermal.h> 28 29 #include <arm/mainbus/mainbus.h> 30 31 int mainbus_match(struct device *, void *, void *); 32 void mainbus_attach(struct device *, struct device *, void *); 33 34 void mainbus_attach_node(struct device *, int, cfmatch_t); 35 int mainbus_match_status(struct device *, void *, void *); 36 void mainbus_attach_cpus(struct device *, cfmatch_t); 37 int mainbus_match_primary(struct device *, void *, void *); 38 int mainbus_match_secondary(struct device *, void *, void *); 39 void mainbus_attach_framebuffer(struct device *); 40 41 struct mainbus_softc { 42 struct device sc_dev; 43 int sc_node; 44 bus_space_tag_t sc_iot; 45 bus_dma_tag_t sc_dmat; 46 int sc_acells; 47 int sc_scells; 48 int *sc_ranges; 49 int sc_rangeslen; 50 int sc_early; 51 }; 52 53 const struct cfattach mainbus_ca = { 54 sizeof(struct mainbus_softc), mainbus_match, mainbus_attach 55 }; 56 57 struct cfdriver mainbus_cd = { 58 NULL, "mainbus", DV_DULL 59 }; 60 61 struct arm32_bus_dma_tag mainbus_dma_tag = { 62 NULL, 63 _bus_dmamap_create, 64 _bus_dmamap_destroy, 65 _bus_dmamap_load, 66 _bus_dmamap_load_mbuf, 67 _bus_dmamap_load_uio, 68 _bus_dmamap_load_raw, 69 _bus_dmamap_load_buffer, 70 _bus_dmamap_unload, 71 _bus_dmamap_sync, 72 _bus_dmamem_alloc, 73 _bus_dmamem_free, 74 _bus_dmamem_map, 75 _bus_dmamem_unmap, 76 _bus_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 extern struct bus_space armv7_bs_tag; 90 void platform_init_mainbus(struct device *); 91 92 void 93 mainbus_attach(struct device *parent, struct device *self, void *aux) 94 { 95 struct mainbus_softc *sc = (struct mainbus_softc *)self; 96 char prop[128]; 97 int node, len; 98 99 arm_intr_init_fdt(); 100 101 sc->sc_node = OF_peer(0); 102 sc->sc_iot = &armv7_bs_tag; 103 sc->sc_dmat = &mainbus_dma_tag; 104 sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 105 sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 106 107 len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop)); 108 if (len > 0) { 109 printf(": %s\n", prop); 110 hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 111 if (hw_prod) 112 strlcpy(hw_prod, prop, len); 113 } else 114 printf(": unknown model\n"); 115 116 len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop)); 117 if (len > 0) { 118 hw_serial = malloc(len, M_DEVBUF, M_NOWAIT); 119 if (hw_serial) 120 strlcpy(hw_serial, prop, len); 121 } 122 123 /* Attach primary CPU first. */ 124 mainbus_attach_cpus(self, mainbus_match_primary); 125 126 platform_init_mainbus(self); 127 128 sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 129 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 130 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 131 OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 132 sc->sc_rangeslen); 133 } 134 135 /* Scan the whole tree. */ 136 sc->sc_early = 1; 137 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 138 mainbus_attach_node(self, node, NULL); 139 140 sc->sc_early = 0; 141 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 142 mainbus_attach_node(self, node, NULL); 143 144 mainbus_attach_framebuffer(self); 145 146 /* Attach secondary CPUs. */ 147 mainbus_attach_cpus(self, mainbus_match_secondary); 148 149 thermal_init(); 150 } 151 152 /* 153 * Look for a driver that wants to be attached to this node. 154 */ 155 void 156 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 157 { 158 struct mainbus_softc *sc = (struct mainbus_softc *)self; 159 struct fdt_attach_args fa; 160 int i, len, line; 161 uint32_t *cell, *reg; 162 163 memset(&fa, 0, sizeof(fa)); 164 fa.fa_name = ""; 165 fa.fa_node = node; 166 fa.fa_iot = sc->sc_iot; 167 fa.fa_dmat = sc->sc_dmat; 168 fa.fa_acells = sc->sc_acells; 169 fa.fa_scells = sc->sc_scells; 170 171 len = OF_getproplen(node, "reg"); 172 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 173 if (len > 0 && (len % line) == 0) { 174 reg = malloc(len, M_TEMP, M_WAITOK); 175 OF_getpropintarray(node, "reg", reg, len); 176 177 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 178 M_DEVBUF, M_WAITOK); 179 fa.fa_nreg = (len / line); 180 181 for (i = 0, cell = reg; i < len / line; i++) { 182 if (sc->sc_acells >= 1) 183 fa.fa_reg[i].addr = cell[0]; 184 if (sc->sc_acells == 2) { 185 fa.fa_reg[i].addr <<= 32; 186 fa.fa_reg[i].addr |= cell[1]; 187 } 188 cell += sc->sc_acells; 189 if (sc->sc_scells >= 1) 190 fa.fa_reg[i].size = cell[0]; 191 if (sc->sc_scells == 2) { 192 fa.fa_reg[i].size <<= 32; 193 fa.fa_reg[i].size |= cell[1]; 194 } 195 cell += sc->sc_scells; 196 } 197 198 free(reg, M_TEMP, len); 199 } 200 201 len = OF_getproplen(node, "interrupts"); 202 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 203 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 204 fa.fa_nintr = len / sizeof(uint32_t); 205 206 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 207 } 208 209 if (submatch == NULL) 210 submatch = mainbus_match_status; 211 config_found_sm(self, &fa, NULL, submatch); 212 213 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 214 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 215 } 216 217 int 218 mainbus_match_status(struct device *parent, void *match, void *aux) 219 { 220 struct mainbus_softc *sc = (struct mainbus_softc *)parent; 221 struct fdt_attach_args *fa = aux; 222 struct cfdata *cf = match; 223 char buf[32]; 224 225 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 226 strcmp(buf, "disabled") == 0) 227 return 0; 228 229 if (cf->cf_loc[0] == sc->sc_early) 230 return (*cf->cf_attach->ca_match)(parent, match, aux); 231 return 0; 232 } 233 234 void 235 mainbus_attach_cpus(struct device *self, cfmatch_t match) 236 { 237 struct mainbus_softc *sc = (struct mainbus_softc *)self; 238 int node = OF_finddevice("/cpus"); 239 int acells, scells; 240 241 if (node == 0) 242 return; 243 244 acells = sc->sc_acells; 245 scells = sc->sc_scells; 246 sc->sc_acells = OF_getpropint(node, "#address-cells", 1); 247 sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 248 249 for (node = OF_child(node); node != 0; node = OF_peer(node)) 250 mainbus_attach_node(self, node, match); 251 252 sc->sc_acells = acells; 253 sc->sc_scells = scells; 254 } 255 256 int 257 mainbus_match_primary(struct device *parent, void *match, void *aux) 258 { 259 struct fdt_attach_args *fa = aux; 260 struct cfdata *cf = match; 261 uint32_t mpidr; 262 263 __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 264 265 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF)) 266 return 0; 267 268 return (*cf->cf_attach->ca_match)(parent, match, aux); 269 } 270 271 int 272 mainbus_match_secondary(struct device *parent, void *match, void *aux) 273 { 274 struct fdt_attach_args *fa = aux; 275 struct cfdata *cf = match; 276 uint32_t mpidr; 277 278 __asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr)); 279 280 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF)) 281 return 0; 282 283 return (*cf->cf_attach->ca_match)(parent, match, aux); 284 } 285 286 void 287 mainbus_attach_framebuffer(struct device *self) 288 { 289 int node = OF_finddevice("/chosen"); 290 291 if (node == 0) 292 return; 293 294 for (node = OF_child(node); node != 0; node = OF_peer(node)) 295 mainbus_attach_node(self, node, NULL); 296 } 297 298 /* 299 * Legacy support for SoCs that do not fully use FDT. 300 */ 301 void 302 mainbus_legacy_found(struct device *self, char *name) 303 { 304 union mainbus_attach_args ma; 305 306 memset(&ma, 0, sizeof(ma)); 307 ma.ma_name = name; 308 309 config_found(self, &ma, NULL); 310 } 311