1 /* $OpenBSD: mainbus.c,v 1.7 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 <machine/cpufunc.h> 26 #include <machine/fdt.h> 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/fdt.h> 29 #include <dev/ofw/ofw_thermal.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 40 struct mainbus_softc { 41 struct device sc_dev; 42 int sc_node; 43 bus_space_tag_t sc_iot; 44 bus_dma_tag_t sc_dmat; 45 int sc_acells; 46 int sc_scells; 47 int *sc_ranges; 48 int sc_rangeslen; 49 int sc_early; 50 int sc_early_nodes[64]; 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 bus_space mainbus_bus_space = { 62 ._space_read_1 = generic_space_read_1, 63 ._space_write_1 = generic_space_write_1, 64 ._space_read_2 = generic_space_read_2, 65 ._space_write_2 = generic_space_write_2, 66 ._space_read_4 = generic_space_read_4, 67 ._space_write_4 = generic_space_write_4, 68 ._space_read_8 = generic_space_read_8, 69 ._space_write_8 = generic_space_write_8, 70 ._space_read_raw_2 = generic_space_read_raw_2, 71 ._space_write_raw_2 = generic_space_write_raw_2, 72 ._space_read_raw_4 = generic_space_read_raw_4, 73 ._space_write_raw_4 = generic_space_write_raw_4, 74 ._space_read_raw_8 = generic_space_read_raw_8, 75 ._space_write_raw_8 = generic_space_write_raw_8, 76 ._space_map = generic_space_map, 77 ._space_unmap = generic_space_unmap, 78 ._space_subregion = generic_space_region, 79 ._space_vaddr = generic_space_vaddr, 80 ._space_mmap = generic_space_mmap 81 }; 82 83 struct machine_bus_dma_tag mainbus_dma_tag = { 84 NULL, 85 0, 86 _dmamap_create, 87 _dmamap_destroy, 88 _dmamap_load, 89 _dmamap_load_mbuf, 90 _dmamap_load_uio, 91 _dmamap_load_raw, 92 _dmamap_load_buffer, 93 _dmamap_unload, 94 _dmamap_sync, 95 _dmamem_alloc, 96 _dmamem_free, 97 _dmamem_map, 98 _dmamem_unmap, 99 _dmamem_mmap, 100 }; 101 102 /* 103 * Mainbus takes care of FDT and non-FDT machines, so we 104 * always attach. 105 */ 106 int 107 mainbus_match(struct device *parent, void *cfdata, void *aux) 108 { 109 return (1); 110 } 111 112 void 113 mainbus_attach(struct device *parent, struct device *self, void *aux) 114 { 115 struct mainbus_softc *sc = (struct mainbus_softc *)self; 116 char prop[128]; 117 int node, len; 118 119 sc->sc_node = OF_peer(0); 120 sc->sc_iot = &mainbus_bus_space; 121 sc->sc_dmat = &mainbus_dma_tag; 122 sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1); 123 sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1); 124 125 len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop)); 126 if (len > 0) { 127 printf(": %s\n", prop); 128 hw_prod = malloc(len, M_DEVBUF, M_NOWAIT); 129 if (hw_prod) 130 strlcpy(hw_prod, prop, len); 131 } else 132 printf(": unknown model\n"); 133 134 len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop)); 135 if (len > 0) { 136 hw_serial = malloc(len, M_DEVBUF, M_NOWAIT); 137 if (hw_serial) 138 strlcpy(hw_serial, prop, len); 139 } 140 141 /* Attach primary CPU first. */ 142 mainbus_attach_cpus(self, mainbus_match_primary); 143 144 /* Attach secondary CPUs. */ 145 mainbus_attach_cpus(self, mainbus_match_secondary); 146 147 sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges"); 148 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 149 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 150 OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges, 151 sc->sc_rangeslen); 152 } 153 154 /* Scan the whole tree. */ 155 sc->sc_early = 1; 156 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 157 mainbus_attach_node(self, node, NULL); 158 159 sc->sc_early = 0; 160 for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node)) 161 mainbus_attach_node(self, node, NULL); 162 } 163 164 int 165 mainbus_print(void *aux, const char *pnp) 166 { 167 struct fdt_attach_args *fa = aux; 168 char buf[32]; 169 170 if (!pnp) 171 return (QUIET); 172 173 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 174 strcmp(buf, "disabled") == 0) 175 return (QUIET); 176 177 if (OF_getprop(fa->fa_node, "name", buf, sizeof(buf)) > 0) { 178 buf[sizeof(buf) - 1] = 0; 179 if (strcmp(buf, "aliases") == 0 || 180 strcmp(buf, "chosen") == 0 || 181 strcmp(buf, "cpus") == 0 || 182 strcmp(buf, "memory") == 0 || 183 strcmp(buf, "reserved-memory") == 0 || 184 strcmp(buf, "thermal-zones") == 0 || 185 strncmp(buf, "__", 2) == 0) 186 return (QUIET); 187 printf("\"%s\"", buf); 188 } else 189 printf("node %u", fa->fa_node); 190 191 printf(" at %s", pnp); 192 193 return (UNCONF); 194 } 195 196 /* 197 * Look for a driver that wants to be attached to this node. 198 */ 199 void 200 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch) 201 { 202 struct mainbus_softc *sc = (struct mainbus_softc *)self; 203 struct fdt_attach_args fa; 204 int i, len, line; 205 uint32_t *cell, *reg; 206 struct device *child; 207 cfprint_t print = NULL; 208 209 /* Skip if already attached early. */ 210 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 211 if (sc->sc_early_nodes[i] == node) 212 return; 213 if (sc->sc_early_nodes[i] == 0) 214 break; 215 } 216 217 memset(&fa, 0, sizeof(fa)); 218 fa.fa_name = ""; 219 fa.fa_node = node; 220 fa.fa_iot = sc->sc_iot; 221 fa.fa_dmat = sc->sc_dmat; 222 fa.fa_acells = sc->sc_acells; 223 fa.fa_scells = sc->sc_scells; 224 225 len = OF_getproplen(node, "reg"); 226 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 227 if (len > 0 && (len % line) == 0) { 228 reg = malloc(len, M_TEMP, M_WAITOK); 229 OF_getpropintarray(node, "reg", reg, len); 230 231 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 232 M_DEVBUF, M_WAITOK); 233 fa.fa_nreg = (len / line); 234 235 for (i = 0, cell = reg; i < len / line; i++) { 236 if (sc->sc_acells >= 1) 237 fa.fa_reg[i].addr = cell[0]; 238 if (sc->sc_acells == 2) { 239 fa.fa_reg[i].addr <<= 32; 240 fa.fa_reg[i].addr |= cell[1]; 241 } 242 cell += sc->sc_acells; 243 if (sc->sc_scells >= 1) 244 fa.fa_reg[i].size = cell[0]; 245 if (sc->sc_scells == 2) { 246 fa.fa_reg[i].size <<= 32; 247 fa.fa_reg[i].size |= cell[1]; 248 } 249 cell += sc->sc_scells; 250 } 251 252 free(reg, M_TEMP, len); 253 } 254 255 len = OF_getproplen(node, "interrupts"); 256 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 257 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 258 fa.fa_nintr = len / sizeof(uint32_t); 259 260 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 261 } 262 263 #ifdef notyet 264 if (OF_getproplen(node, "dma-coherent") >= 0) { 265 fa.fa_dmat = malloc(sizeof(*sc->sc_dmat), 266 M_DEVBUF, M_WAITOK | M_ZERO); 267 memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat)); 268 fa.fa_dmat->_flags |= BUS_DMA_COHERENT; 269 } 270 #endif 271 272 if (submatch == NULL && sc->sc_early == 0) 273 print = mainbus_print; 274 if (submatch == NULL) 275 submatch = mainbus_match_status; 276 277 child = config_found_sm(self, &fa, print, submatch); 278 279 /* Record nodes that we attach early. */ 280 if (child && sc->sc_early) { 281 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 282 if (sc->sc_early_nodes[i] != 0) 283 continue; 284 sc->sc_early_nodes[i] = node; 285 break; 286 } 287 } 288 289 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 290 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 291 } 292 293 int 294 mainbus_match_status(struct device *parent, void *match, void *aux) 295 { 296 struct mainbus_softc *sc = (struct mainbus_softc *)parent; 297 struct fdt_attach_args *fa = aux; 298 struct cfdata *cf = match; 299 char buf[32]; 300 301 if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 && 302 strcmp(buf, "disabled") == 0) 303 return 0; 304 305 if (cf->cf_loc[0] == sc->sc_early) 306 return (*cf->cf_attach->ca_match)(parent, match, aux); 307 308 return 0; 309 } 310 311 void 312 mainbus_attach_cpus(struct device *self, cfmatch_t match) 313 { 314 struct mainbus_softc *sc = (struct mainbus_softc *)self; 315 int node = OF_finddevice("/cpus"); 316 int acells, scells; 317 char buf[32]; 318 319 if (node == 0) 320 return; 321 322 acells = sc->sc_acells; 323 scells = sc->sc_scells; 324 sc->sc_acells = OF_getpropint(node, "#address-cells", 1); 325 sc->sc_scells = OF_getpropint(node, "#size-cells", 0); 326 327 ncpusfound = 0; 328 for (node = OF_child(node); node != 0; node = OF_peer(node)) { 329 if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 && 330 strcmp(buf, "cpu") == 0) 331 ncpusfound++; 332 333 mainbus_attach_node(self, node, match); 334 } 335 336 sc->sc_acells = acells; 337 sc->sc_scells = scells; 338 } 339 340 int 341 mainbus_match_primary(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 != mfpir()) 347 return 0; 348 349 return (*cf->cf_attach->ca_match)(parent, match, aux); 350 } 351 352 int 353 mainbus_match_secondary(struct device *parent, void *match, void *aux) 354 { 355 struct fdt_attach_args *fa = aux; 356 struct cfdata *cf = match; 357 358 if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == mfpir()) 359 return 0; 360 361 return (*cf->cf_attach->ca_match)(parent, match, aux); 362 } 363