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