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