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