1/* $OpenBSD: simplebus.c,v 1.18 2023/09/22 01:10:43 jsg Exp $ */ 2/* 3 * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/kernel.h> 21#include <sys/device.h> 22#include <sys/malloc.h> 23 24#include <machine/fdt.h> 25#include <dev/ofw/openfirm.h> 26#include <dev/ofw/fdt.h> 27#include <dev/ofw/ofw_misc.h> 28 29#include <machine/fdt.h> 30#include <machine/simplebusvar.h> 31 32int simplebus_match(struct device *, void *, void *); 33void simplebus_attach(struct device *, struct device *, void *); 34 35void simplebus_attach_node(struct device *, int); 36int simplebus_bs_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, 37 bus_space_handle_t *); 38paddr_t simplebus_bs_mmap(bus_space_tag_t, bus_addr_t, off_t, int, int); 39int simplebus_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *, 40 bus_size_t, struct proc *, int, paddr_t *, int *, int); 41int simplebus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, 42 bus_dma_segment_t *, int, bus_size_t, int); 43 44const struct cfattach simplebus_ca = { 45 sizeof(struct simplebus_softc), simplebus_match, simplebus_attach 46}; 47 48struct cfdriver simplebus_cd = { 49 NULL, "simplebus", DV_DULL 50}; 51 52/* 53 * Simplebus is a generic bus with no special casings. 54 */ 55int 56simplebus_match(struct device *parent, void *cfdata, void *aux) 57{ 58 struct fdt_attach_args *fa = (struct fdt_attach_args *)aux; 59 60 if (fa->fa_node == 0) 61 return (0); 62 63 /* Qualcomm GENI can mostly be treated as simple-bus. */ 64 if (OF_is_compatible(fa->fa_node, "qcom,geni-se-qup")) 65 return (1); 66 67 if (!OF_is_compatible(fa->fa_node, "simple-bus")) 68 return (0); 69 70 return (1); 71} 72 73void 74simplebus_attach(struct device *parent, struct device *self, void *aux) 75{ 76 struct simplebus_softc *sc = (struct simplebus_softc *)self; 77 struct fdt_attach_args *fa = (struct fdt_attach_args *)aux; 78 char name[32]; 79 int node; 80 81 sc->sc_node = fa->fa_node; 82 sc->sc_iot = fa->fa_iot; 83 sc->sc_dmat = fa->fa_dmat; 84 sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells", 85 fa->fa_acells); 86 sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells", 87 fa->fa_scells); 88 sc->sc_pacells = fa->fa_acells; 89 sc->sc_pscells = fa->fa_scells; 90 91 if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) { 92 name[sizeof(name) - 1] = 0; 93 printf(": \"%s\"", name); 94 } 95 96 printf("\n"); 97 98 memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus)); 99 sc->sc_bus.bus_private = sc; 100 sc->sc_bus._space_map = simplebus_bs_map; 101 sc->sc_bus._space_mmap = simplebus_bs_mmap; 102 103 sc->sc_rangeslen = OF_getproplen(sc->sc_node, "ranges"); 104 if (sc->sc_rangeslen > 0 && 105 (sc->sc_rangeslen % sizeof(uint32_t)) == 0) { 106 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 107 OF_getpropintarray(sc->sc_node, "ranges", sc->sc_ranges, 108 sc->sc_rangeslen); 109 } 110 111 memcpy(&sc->sc_dma, sc->sc_dmat, sizeof(sc->sc_dma)); 112 sc->sc_dma._dmamap_load_buffer = simplebus_dmamap_load_buffer; 113 sc->sc_dma._dmamap_load_raw = simplebus_dmamap_load_raw; 114 sc->sc_dma._cookie = sc; 115 116 sc->sc_dmarangeslen = OF_getproplen(sc->sc_node, "dma-ranges"); 117 if (sc->sc_dmarangeslen > 0 && 118 (sc->sc_dmarangeslen % sizeof(uint32_t)) == 0) { 119 sc->sc_dmaranges = malloc(sc->sc_dmarangeslen, 120 M_TEMP, M_WAITOK); 121 OF_getpropintarray(sc->sc_node, "dma-ranges", 122 sc->sc_dmaranges, sc->sc_dmarangeslen); 123 } 124 125 /* 126 * The device tree provided by the Raspberry Pi firmware lacks 127 * a "dma-ranges" option. So provide the information until 128 * that gets fixed. 129 */ 130 if (sc->sc_dmaranges == NULL) { 131 node = OF_parent(sc->sc_node); 132 if (OF_is_compatible(node, "brcm,bcm2709")) { 133 sc->sc_dmarangeslen = 3 * sizeof(uint32_t); 134 sc->sc_dmaranges = malloc(sc->sc_dmarangeslen, 135 M_TEMP, M_WAITOK); 136 sc->sc_dmaranges[0] = 0xc0000000; 137 sc->sc_dmaranges[1] = 0x00000000; 138 sc->sc_dmaranges[2] = 0x3f000000; 139 } 140 } 141 142 /* Scan the whole tree. */ 143 for (sc->sc_early = 2; sc->sc_early >= 0; sc->sc_early--) { 144 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 145 simplebus_attach_node(self, node); 146 } 147} 148 149int 150simplebus_submatch(struct device *self, void *match, void *aux) 151{ 152 struct simplebus_softc *sc = (struct simplebus_softc *)self; 153 struct cfdata *cf = match; 154 155 if (cf->cf_loc[0] == sc->sc_early) 156 return (*cf->cf_attach->ca_match)(self, match, aux); 157 return 0; 158} 159 160int 161simplebus_print(void *aux, const char *pnp) 162{ 163 struct fdt_attach_args *fa = aux; 164 char name[32]; 165 166 if (!pnp) 167 return (QUIET); 168 169 if (OF_getprop(fa->fa_node, "name", name, sizeof(name)) > 0) { 170 name[sizeof(name) - 1] = 0; 171 printf("\"%s\"", name); 172 } else 173 printf("node %u", fa->fa_node); 174 175 printf(" at %s", pnp); 176 177 return (UNCONF); 178} 179 180/* 181 * Look for a driver that wants to be attached to this node. 182 */ 183void 184simplebus_attach_node(struct device *self, int node) 185{ 186 struct simplebus_softc *sc = (struct simplebus_softc *)self; 187 struct fdt_attach_args fa; 188 char buf[32]; 189 int i, len, line; 190 uint32_t *cell, *reg; 191 struct device *child; 192 193 if (OF_getproplen(node, "compatible") <= 0) 194 return; 195 196 if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && 197 strcmp(buf, "disabled") == 0) 198 return; 199 200 /* Skip if already attached early. */ 201 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 202 if (sc->sc_early_nodes[i] == node) 203 return; 204 if (sc->sc_early_nodes[i] == 0) 205 break; 206 } 207 208 memset(&fa, 0, sizeof(fa)); 209 fa.fa_name = ""; 210 fa.fa_node = node; 211 fa.fa_iot = &sc->sc_bus; 212 fa.fa_dmat = &sc->sc_dma; 213 fa.fa_acells = sc->sc_acells; 214 fa.fa_scells = sc->sc_scells; 215 216 len = OF_getproplen(node, "reg"); 217 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 218 if (len > 0 && line > 0 && (len % line) == 0) { 219 reg = malloc(len, M_TEMP, M_WAITOK); 220 OF_getpropintarray(node, "reg", reg, len); 221 222 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 223 M_DEVBUF, M_WAITOK | M_ZERO); 224 fa.fa_nreg = (len / line); 225 226 for (i = 0, cell = reg; i < len / line; i++) { 227 if (sc->sc_acells >= 1) 228 fa.fa_reg[i].addr = cell[0]; 229 if (sc->sc_acells == 2) { 230 fa.fa_reg[i].addr <<= 32; 231 fa.fa_reg[i].addr |= cell[1]; 232 } 233 cell += sc->sc_acells; 234 if (sc->sc_scells >= 1) 235 fa.fa_reg[i].size = cell[0]; 236 if (sc->sc_scells == 2) { 237 fa.fa_reg[i].size <<= 32; 238 fa.fa_reg[i].size |= cell[1]; 239 } 240 cell += sc->sc_scells; 241 } 242 243 free(reg, M_TEMP, len); 244 } 245 246 len = OF_getproplen(node, "interrupts"); 247 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 248 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 249 fa.fa_nintr = len / sizeof(uint32_t); 250 251 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 252 } 253 254 if (OF_getproplen(node, "dma-coherent") >= 0) { 255 fa.fa_dmat = malloc(sizeof(sc->sc_dma), 256 M_DEVBUF, M_WAITOK | M_ZERO); 257 memcpy(fa.fa_dmat, &sc->sc_dma, sizeof(sc->sc_dma)); 258 fa.fa_dmat->_flags |= BUS_DMA_COHERENT; 259 } 260 261 fa.fa_dmat = iommu_device_map(fa.fa_node, fa.fa_dmat); 262 263 child = config_found_sm(self, &fa, sc->sc_early ? NULL : 264 simplebus_print, simplebus_submatch); 265 266 /* Record nodes that we attach early. */ 267 if (child && sc->sc_early) { 268 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 269 if (sc->sc_early_nodes[i] != 0) 270 continue; 271 sc->sc_early_nodes[i] = node; 272 break; 273 } 274 } 275 276 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 277 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 278} 279 280/* 281 * Translate memory address if needed. 282 */ 283int 284simplebus_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, 285 int flag, bus_space_handle_t *bshp) 286{ 287 struct simplebus_softc *sc = t->bus_private; 288 uint64_t addr, rfrom, rto, rsize; 289 uint32_t *range; 290 int parent, rlen, rone; 291 292 addr = bpa; 293 parent = OF_parent(sc->sc_node); 294 if (parent == 0) 295 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 296 297 if (sc->sc_rangeslen < 0) 298 return EINVAL; 299 if (sc->sc_rangeslen == 0) 300 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 301 302 rlen = sc->sc_rangeslen / sizeof(uint32_t); 303 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 304 305 /* For each range. */ 306 for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) { 307 /* Extract from and size, so we can see if we fit. */ 308 rfrom = range[0]; 309 if (sc->sc_acells == 2) 310 rfrom = (rfrom << 32) + range[1]; 311 rsize = range[sc->sc_acells + sc->sc_pacells]; 312 if (sc->sc_scells == 2) 313 rsize = (rsize << 32) + 314 range[sc->sc_acells + sc->sc_pacells + 1]; 315 316 /* Try next, if we're not in the range. */ 317 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 318 continue; 319 320 /* All good, extract to address and translate. */ 321 rto = range[sc->sc_acells]; 322 if (sc->sc_pacells == 2) 323 rto = (rto << 32) + range[sc->sc_acells + 1]; 324 325 addr -= rfrom; 326 addr += rto; 327 328 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 329 } 330 331 return ESRCH; 332} 333 334paddr_t 335simplebus_bs_mmap(bus_space_tag_t t, bus_addr_t bpa, off_t off, 336 int prot, int flags) 337{ 338 struct simplebus_softc *sc = t->bus_private; 339 uint64_t addr, rfrom, rto, rsize; 340 uint32_t *range; 341 int parent, rlen, rone; 342 343 addr = bpa; 344 parent = OF_parent(sc->sc_node); 345 if (parent == 0) 346 return bus_space_mmap(sc->sc_iot, addr, off, prot, flags); 347 348 if (sc->sc_rangeslen < 0) 349 return EINVAL; 350 if (sc->sc_rangeslen == 0) 351 return bus_space_mmap(sc->sc_iot, addr, off, prot, flags); 352 353 rlen = sc->sc_rangeslen / sizeof(uint32_t); 354 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 355 356 /* For each range. */ 357 for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) { 358 /* Extract from and size, so we can see if we fit. */ 359 rfrom = range[0]; 360 if (sc->sc_acells == 2) 361 rfrom = (rfrom << 32) + range[1]; 362 rsize = range[sc->sc_acells + sc->sc_pacells]; 363 if (sc->sc_scells == 2) 364 rsize = (rsize << 32) + 365 range[sc->sc_acells + sc->sc_pacells + 1]; 366 367 /* Try next, if we're not in the range. */ 368 if (addr < rfrom || addr >= (rfrom + rsize)) 369 continue; 370 371 /* All good, extract to address and translate. */ 372 rto = range[sc->sc_acells]; 373 if (sc->sc_pacells == 2) 374 rto = (rto << 32) + range[sc->sc_acells + 1]; 375 376 addr -= rfrom; 377 addr += rto; 378 379 return bus_space_mmap(sc->sc_iot, addr, off, prot, flags); 380 } 381 382 return -1; 383} 384 385int 386simplebus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 387 bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp, 388 int *segp, int first) 389{ 390 struct simplebus_softc *sc = t->_cookie; 391 int rlen, rone, seg; 392 int firstseg = *segp; 393 int error; 394 395 error = sc->sc_dmat->_dmamap_load_buffer(sc->sc_dmat, map, buf, buflen, 396 p, flags, lastaddrp, segp, first); 397 if (error) 398 return error; 399 400 if (sc->sc_dmaranges == NULL) 401 return 0; 402 403 rlen = sc->sc_dmarangeslen / sizeof(uint32_t); 404 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 405 406 /* For each segment. */ 407 for (seg = firstseg; seg <= *segp; seg++) { 408 uint64_t addr, size, rfrom, rto, rsize; 409 uint32_t *range; 410 411 addr = map->dm_segs[seg].ds_addr; 412 size = map->dm_segs[seg].ds_len; 413 414 /* For each range. */ 415 for (range = sc->sc_dmaranges; rlen >= rone; 416 rlen -= rone, range += rone) { 417 /* Extract from and size, so we can see if we fit. */ 418 rfrom = range[sc->sc_acells]; 419 if (sc->sc_pacells == 2) 420 rfrom = (rfrom << 32) + range[sc->sc_acells + 1]; 421 422 rsize = range[sc->sc_acells + sc->sc_pacells]; 423 if (sc->sc_scells == 2) 424 rsize = (rsize << 32) + 425 range[sc->sc_acells + sc->sc_pacells + 1]; 426 427 /* Try next, if we're not in the range. */ 428 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 429 continue; 430 431 /* All good, extract to address and translate. */ 432 rto = range[0]; 433 if (sc->sc_acells == 2) 434 rto = (rto << 32) + range[1]; 435 436 map->dm_segs[seg].ds_addr -= rfrom; 437 map->dm_segs[seg].ds_addr += rto; 438 break; 439 } 440 } 441 442 return 0; 443} 444 445int 446simplebus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, 447 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 448{ 449 struct simplebus_softc *sc = t->_cookie; 450 int rlen, rone, seg; 451 int error; 452 453 error = sc->sc_dmat->_dmamap_load_raw(sc->sc_dmat, map, 454 segs, nsegs, size, flags); 455 if (error) 456 return error; 457 458 if (sc->sc_dmaranges == NULL) 459 return 0; 460 461 rlen = sc->sc_dmarangeslen / sizeof(uint32_t); 462 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 463 464 /* For each segment. */ 465 for (seg = 0; seg < map->dm_nsegs; seg++) { 466 uint64_t addr, size, rfrom, rto, rsize; 467 uint32_t *range; 468 469 addr = map->dm_segs[seg].ds_addr; 470 size = map->dm_segs[seg].ds_len; 471 472 /* For each range. */ 473 for (range = sc->sc_dmaranges; rlen >= rone; 474 rlen -= rone, range += rone) { 475 /* Extract from and size, so we can see if we fit. */ 476 rfrom = range[sc->sc_acells]; 477 if (sc->sc_pacells == 2) 478 rfrom = (rfrom << 32) + range[sc->sc_acells + 1]; 479 480 rsize = range[sc->sc_acells + sc->sc_pacells]; 481 if (sc->sc_scells == 2) 482 rsize = (rsize << 32) + 483 range[sc->sc_acells + sc->sc_pacells + 1]; 484 485 /* Try next, if we're not in the range. */ 486 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 487 continue; 488 489 /* All good, extract to address and translate. */ 490 rto = range[0]; 491 if (sc->sc_acells == 2) 492 rto = (rto << 32) + range[1]; 493 494 map->dm_segs[seg].ds_addr -= rfrom; 495 map->dm_segs[seg].ds_addr += rto; 496 break; 497 } 498 } 499 500 return 0; 501} 502