1 /* $OpenBSD: simplebus.c,v 1.20 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 <dev/ofw/openfirm.h> 25 #include <dev/ofw/fdt.h> 26 #include <dev/ofw/ofw_clock.h> 27 #include <dev/ofw/ofw_power.h> 28 29 #include <machine/fdt.h> 30 #include <machine/simplebusvar.h> 31 32 int simplebus_match(struct device *, void *, void *); 33 void simplebus_attach(struct device *, struct device *, void *); 34 35 void simplebus_attach_node(struct device *, int); 36 int simplebus_bs_map(void *, uint64_t, bus_size_t, int, bus_space_handle_t *); 37 int simplebus_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *, 38 bus_size_t, struct proc *, int, paddr_t *, int *, int); 39 int simplebus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, 40 bus_dma_segment_t *, int, bus_size_t, int); 41 42 const struct cfattach simplebus_ca = { 43 sizeof(struct simplebus_softc), simplebus_match, simplebus_attach 44 }; 45 46 struct cfdriver simplebus_cd = { 47 NULL, "simplebus", DV_DULL 48 }; 49 50 /* 51 * Simplebus is a generic bus with no special casings. 52 */ 53 int 54 simplebus_match(struct device *parent, void *cfdata, void *aux) 55 { 56 struct fdt_attach_args *fa = (struct fdt_attach_args *)aux; 57 58 if (fa->fa_node == 0) 59 return (0); 60 61 return (OF_is_compatible(fa->fa_node, "simple-bus") || 62 OF_is_compatible(fa->fa_node, "simple-pm-bus")); 63 } 64 65 void 66 simplebus_attach(struct device *parent, struct device *self, void *aux) 67 { 68 struct simplebus_softc *sc = (struct simplebus_softc *)self; 69 struct fdt_attach_args *fa = (struct fdt_attach_args *)aux; 70 char name[32]; 71 int node; 72 73 sc->sc_node = fa->fa_node; 74 sc->sc_iot = fa->fa_iot; 75 sc->sc_dmat = fa->fa_dmat; 76 sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells", 77 fa->fa_acells); 78 sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells", 79 fa->fa_scells); 80 sc->sc_pacells = fa->fa_acells; 81 sc->sc_pscells = fa->fa_scells; 82 83 if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) { 84 name[sizeof(name) - 1] = 0; 85 printf(": \"%s\"", name); 86 } 87 88 printf("\n"); 89 90 memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus)); 91 sc->sc_bus.bs_cookie = sc; 92 sc->sc_bus.bs_map = simplebus_bs_map; 93 94 sc->sc_rangeslen = OF_getproplen(sc->sc_node, "ranges"); 95 if (sc->sc_rangeslen > 0 && 96 (sc->sc_rangeslen % sizeof(uint32_t)) == 0) { 97 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 98 OF_getpropintarray(sc->sc_node, "ranges", sc->sc_ranges, 99 sc->sc_rangeslen); 100 } 101 102 memcpy(&sc->sc_dma, sc->sc_dmat, sizeof(sc->sc_dma)); 103 sc->sc_dma._dmamap_load_buffer = simplebus_dmamap_load_buffer; 104 sc->sc_dma._dmamap_load_raw = simplebus_dmamap_load_raw; 105 sc->sc_dma._cookie = sc; 106 107 sc->sc_dmarangeslen = OF_getproplen(sc->sc_node, "dma-ranges"); 108 if (sc->sc_dmarangeslen > 0 && 109 (sc->sc_dmarangeslen % sizeof(uint32_t)) == 0) { 110 sc->sc_dmaranges = malloc(sc->sc_dmarangeslen, 111 M_TEMP, M_WAITOK); 112 OF_getpropintarray(sc->sc_node, "dma-ranges", 113 sc->sc_dmaranges, sc->sc_dmarangeslen); 114 } 115 116 if (OF_is_compatible(sc->sc_node, "simple-pm-bus")) { 117 power_domain_enable(sc->sc_node); 118 clock_enable_all(sc->sc_node); 119 } 120 121 /* Scan the whole tree. */ 122 sc->sc_early = 1; 123 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 124 simplebus_attach_node(self, node); 125 126 sc->sc_early = 0; 127 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 128 simplebus_attach_node(self, node); 129 } 130 131 int 132 simplebus_submatch(struct device *self, void *match, void *aux) 133 { 134 struct simplebus_softc *sc = (struct simplebus_softc *)self; 135 struct cfdata *cf = match; 136 137 if (cf->cf_loc[0] == sc->sc_early) 138 return (*cf->cf_attach->ca_match)(self, match, aux); 139 return 0; 140 } 141 142 int 143 simplebus_print(void *aux, const char *pnp) 144 { 145 struct fdt_attach_args *fa = aux; 146 char name[32]; 147 148 if (!pnp) 149 return (QUIET); 150 151 if (OF_getprop(fa->fa_node, "name", name, sizeof(name)) > 0) { 152 name[sizeof(name) - 1] = 0; 153 printf("\"%s\"", name); 154 } else 155 printf("node %u", fa->fa_node); 156 157 printf(" at %s", pnp); 158 159 return (UNCONF); 160 } 161 162 /* 163 * Look for a driver that wants to be attached to this node. 164 */ 165 void 166 simplebus_attach_node(struct device *self, int node) 167 { 168 struct simplebus_softc *sc = (struct simplebus_softc *)self; 169 struct fdt_attach_args fa; 170 char buf[32]; 171 int i, len, line; 172 uint32_t *cell, *reg; 173 struct device *child; 174 175 if (OF_getproplen(node, "compatible") <= 0) 176 return; 177 178 if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && 179 strcmp(buf, "disabled") == 0) 180 return; 181 182 /* Skip if already attached early. */ 183 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 184 if (sc->sc_early_nodes[i] == node) 185 return; 186 if (sc->sc_early_nodes[i] == 0) 187 break; 188 } 189 190 memset(&fa, 0, sizeof(fa)); 191 fa.fa_name = ""; 192 fa.fa_node = node; 193 fa.fa_iot = &sc->sc_bus; 194 fa.fa_dmat = &sc->sc_dma; 195 fa.fa_acells = sc->sc_acells; 196 fa.fa_scells = sc->sc_scells; 197 198 len = OF_getproplen(node, "reg"); 199 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 200 if (len > 0 && line > 0 && (len % line) == 0) { 201 reg = malloc(len, M_TEMP, M_WAITOK); 202 OF_getpropintarray(node, "reg", reg, len); 203 204 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 205 M_DEVBUF, M_WAITOK | M_ZERO); 206 fa.fa_nreg = (len / line); 207 208 for (i = 0, cell = reg; i < len / line; i++) { 209 if (sc->sc_acells >= 1) 210 fa.fa_reg[i].addr = cell[0]; 211 if (sc->sc_acells == 2) { 212 fa.fa_reg[i].addr <<= 32; 213 fa.fa_reg[i].addr |= cell[1]; 214 } 215 cell += sc->sc_acells; 216 if (sc->sc_scells >= 1) 217 fa.fa_reg[i].size = cell[0]; 218 if (sc->sc_scells == 2) { 219 fa.fa_reg[i].size <<= 32; 220 fa.fa_reg[i].size |= cell[1]; 221 } 222 cell += sc->sc_scells; 223 } 224 225 free(reg, M_TEMP, len); 226 } 227 228 len = OF_getproplen(node, "interrupts"); 229 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 230 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 231 fa.fa_nintr = len / sizeof(uint32_t); 232 233 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 234 } 235 236 child = config_found_sm(self, &fa, sc->sc_early ? NULL : 237 simplebus_print, simplebus_submatch); 238 239 /* Record nodes that we attach early. */ 240 if (child && sc->sc_early) { 241 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 242 if (sc->sc_early_nodes[i] != 0) 243 continue; 244 sc->sc_early_nodes[i] = node; 245 break; 246 } 247 } 248 249 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 250 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 251 } 252 253 /* 254 * Translate memory address if needed. 255 */ 256 int 257 simplebus_bs_map(void *t, uint64_t bpa, bus_size_t size, 258 int flag, bus_space_handle_t *bshp) 259 { 260 struct simplebus_softc *sc = (struct simplebus_softc *)t; 261 uint64_t addr, rfrom, rto, rsize; 262 uint32_t *range; 263 int parent, rlen, rone; 264 265 addr = bpa; 266 parent = OF_parent(sc->sc_node); 267 if (parent == 0) 268 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 269 270 if (sc->sc_rangeslen < 0) 271 return EINVAL; 272 if (sc->sc_rangeslen == 0) 273 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 274 275 rlen = sc->sc_rangeslen / sizeof(uint32_t); 276 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 277 278 /* For each range. */ 279 for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) { 280 /* Extract from and size, so we can see if we fit. */ 281 rfrom = range[0]; 282 if (sc->sc_acells == 2) 283 rfrom = (rfrom << 32) + range[1]; 284 rsize = range[sc->sc_acells + sc->sc_pacells]; 285 if (sc->sc_scells == 2) 286 rsize = (rsize << 32) + 287 range[sc->sc_acells + sc->sc_pacells + 1]; 288 289 /* Try next, if we're not in the range. */ 290 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 291 continue; 292 293 /* All good, extract to address and translate. */ 294 rto = range[sc->sc_acells]; 295 if (sc->sc_pacells == 2) 296 rto = (rto << 32) + range[sc->sc_acells + 1]; 297 298 addr -= rfrom; 299 addr += rto; 300 301 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 302 } 303 304 return ESRCH; 305 } 306 307 int 308 simplebus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 309 bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp, 310 int *segp, int first) 311 { 312 struct simplebus_softc *sc = t->_cookie; 313 int rlen, rone, seg; 314 int firstseg = *segp; 315 int error; 316 317 error = sc->sc_dmat->_dmamap_load_buffer(sc->sc_dmat, map, buf, buflen, 318 p, flags, lastaddrp, segp, first); 319 if (error) 320 return error; 321 322 if (sc->sc_dmaranges == NULL) 323 return 0; 324 325 rlen = sc->sc_dmarangeslen / sizeof(uint32_t); 326 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 327 328 /* For each segment. */ 329 for (seg = firstseg; seg <= *segp; seg++) { 330 uint64_t addr, size, rfrom, rto, rsize; 331 uint32_t *range; 332 333 addr = map->dm_segs[seg].ds_addr; 334 size = map->dm_segs[seg].ds_len; 335 336 /* For each range. */ 337 for (range = sc->sc_dmaranges; rlen >= rone; 338 rlen -= rone, range += rone) { 339 /* Extract from and size, so we can see if we fit. */ 340 rfrom = range[sc->sc_acells]; 341 if (sc->sc_pacells == 2) 342 rfrom = (rfrom << 32) + range[sc->sc_acells + 1]; 343 344 rsize = range[sc->sc_acells + sc->sc_pacells]; 345 if (sc->sc_scells == 2) 346 rsize = (rsize << 32) + 347 range[sc->sc_acells + sc->sc_pacells + 1]; 348 349 /* Try next, if we're not in the range. */ 350 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 351 continue; 352 353 /* All good, extract to address and translate. */ 354 rto = range[0]; 355 if (sc->sc_acells == 2) 356 rto = (rto << 32) + range[1]; 357 358 map->dm_segs[seg].ds_addr -= rfrom; 359 map->dm_segs[seg].ds_addr += rto; 360 break; 361 } 362 } 363 364 return 0; 365 } 366 367 int 368 simplebus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, 369 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 370 { 371 struct simplebus_softc *sc = t->_cookie; 372 int rlen, rone, seg; 373 int error; 374 375 error = sc->sc_dmat->_dmamap_load_raw(sc->sc_dmat, map, 376 segs, nsegs, size, flags); 377 if (error) 378 return error; 379 380 if (sc->sc_dmaranges == NULL) 381 return 0; 382 383 rlen = sc->sc_dmarangeslen / sizeof(uint32_t); 384 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 385 386 /* For each segment. */ 387 for (seg = 0; seg < map->dm_nsegs; seg++) { 388 uint64_t addr, size, rfrom, rto, rsize; 389 uint32_t *range; 390 391 addr = map->dm_segs[seg].ds_addr; 392 size = map->dm_segs[seg].ds_len; 393 394 /* For each range. */ 395 for (range = sc->sc_dmaranges; rlen >= rone; 396 rlen -= rone, range += rone) { 397 /* Extract from and size, so we can see if we fit. */ 398 rfrom = range[sc->sc_acells]; 399 if (sc->sc_pacells == 2) 400 rfrom = (rfrom << 32) + range[sc->sc_acells + 1]; 401 402 rsize = range[sc->sc_acells + sc->sc_pacells]; 403 if (sc->sc_scells == 2) 404 rsize = (rsize << 32) + 405 range[sc->sc_acells + sc->sc_pacells + 1]; 406 407 /* Try next, if we're not in the range. */ 408 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 409 continue; 410 411 /* All good, extract to address and translate. */ 412 rto = range[0]; 413 if (sc->sc_acells == 2) 414 rto = (rto << 32) + range[1]; 415 416 map->dm_segs[seg].ds_addr -= rfrom; 417 map->dm_segs[seg].ds_addr += rto; 418 break; 419 } 420 } 421 422 return 0; 423 } 424