1 /* $OpenBSD: simplebus.c,v 1.11 2019/04/16 13:15:31 kettenis 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 27 #include <arm64/fdt.h> 28 #include <arm64/dev/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 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 /* 115 * The device tree provided by the Raspberry Pi firmware lacks 116 * a "dma-ranges" option. So provide the information until 117 * that gets fixed. 118 */ 119 if (sc->sc_dmaranges == NULL) { 120 node = OF_parent(sc->sc_node); 121 if (OF_is_compatible(node, "brcm,bcm2709")) { 122 sc->sc_dmarangeslen = 3 * sizeof(uint32_t); 123 sc->sc_dmaranges = malloc(sc->sc_dmarangeslen, 124 M_TEMP, M_WAITOK); 125 sc->sc_dmaranges[0] = 0xc0000000; 126 sc->sc_dmaranges[1] = 0x00000000; 127 sc->sc_dmaranges[2] = 0x3f000000; 128 } 129 } 130 131 /* Scan the whole tree. */ 132 sc->sc_early = 1; 133 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 134 simplebus_attach_node(self, node); 135 136 sc->sc_early = 0; 137 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 138 simplebus_attach_node(self, node); 139 } 140 141 int 142 simplebus_submatch(struct device *self, void *match, void *aux) 143 { 144 struct simplebus_softc *sc = (struct simplebus_softc *)self; 145 struct cfdata *cf = match; 146 147 if (cf->cf_loc[0] == sc->sc_early) 148 return (*cf->cf_attach->ca_match)(self, match, aux); 149 return 0; 150 } 151 152 int 153 simplebus_print(void *aux, const char *pnp) 154 { 155 struct fdt_attach_args *fa = aux; 156 char name[32]; 157 158 if (!pnp) 159 return (QUIET); 160 161 if (OF_getprop(fa->fa_node, "name", name, sizeof(name)) > 0) { 162 name[sizeof(name) - 1] = 0; 163 printf("\"%s\"", name); 164 } else 165 printf("node %u", fa->fa_node); 166 167 printf(" at %s", pnp); 168 169 return (UNCONF); 170 } 171 172 /* 173 * Look for a driver that wants to be attached to this node. 174 */ 175 void 176 simplebus_attach_node(struct device *self, int node) 177 { 178 struct simplebus_softc *sc = (struct simplebus_softc *)self; 179 struct fdt_attach_args fa; 180 char buf[32]; 181 int i, len, line; 182 uint32_t *cell, *reg; 183 struct device *child; 184 185 if (OF_getproplen(node, "compatible") <= 0) 186 return; 187 188 if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && 189 strcmp(buf, "disabled") == 0) 190 return; 191 192 /* Skip if already attached early. */ 193 for (i = 0; i < nitems(sc->sc_early_nodes); i++) { 194 if (sc->sc_early_nodes[i] == node) 195 return; 196 if (sc->sc_early_nodes[i] == 0) 197 break; 198 } 199 200 memset(&fa, 0, sizeof(fa)); 201 fa.fa_name = ""; 202 fa.fa_node = node; 203 fa.fa_iot = &sc->sc_bus; 204 fa.fa_dmat = &sc->sc_dma; 205 fa.fa_acells = sc->sc_acells; 206 fa.fa_scells = sc->sc_scells; 207 208 len = OF_getproplen(node, "reg"); 209 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 210 if (len > 0 && line > 0 && (len % line) == 0) { 211 reg = malloc(len, M_TEMP, M_WAITOK); 212 OF_getpropintarray(node, "reg", reg, len); 213 214 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 215 M_DEVBUF, M_WAITOK | M_ZERO); 216 fa.fa_nreg = (len / line); 217 218 for (i = 0, cell = reg; i < len / line; i++) { 219 if (sc->sc_acells >= 1) 220 fa.fa_reg[i].addr = cell[0]; 221 if (sc->sc_acells == 2) { 222 fa.fa_reg[i].addr <<= 32; 223 fa.fa_reg[i].addr |= cell[1]; 224 } 225 cell += sc->sc_acells; 226 if (sc->sc_scells >= 1) 227 fa.fa_reg[i].size = cell[0]; 228 if (sc->sc_scells == 2) { 229 fa.fa_reg[i].size <<= 32; 230 fa.fa_reg[i].size |= cell[1]; 231 } 232 cell += sc->sc_scells; 233 } 234 235 free(reg, M_TEMP, len); 236 } 237 238 len = OF_getproplen(node, "interrupts"); 239 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 240 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 241 fa.fa_nintr = len / sizeof(uint32_t); 242 243 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 244 } 245 246 if (OF_getproplen(node, "dma-coherent") >= 0) { 247 fa.fa_dmat = malloc(sizeof(sc->sc_dma), 248 M_DEVBUF, M_WAITOK | M_ZERO); 249 memcpy(fa.fa_dmat, &sc->sc_dma, sizeof(sc->sc_dma)); 250 fa.fa_dmat->_flags |= BUS_DMA_COHERENT; 251 } 252 253 child = config_found_sm(self, &fa, sc->sc_early ? NULL : 254 simplebus_print, simplebus_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 /* 271 * Translate memory address if needed. 272 */ 273 int 274 simplebus_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, 275 int flag, bus_space_handle_t *bshp) 276 { 277 struct simplebus_softc *sc = t->bus_private; 278 uint64_t addr, rfrom, rto, rsize; 279 uint32_t *range; 280 int parent, rlen, rone; 281 282 addr = bpa; 283 parent = OF_parent(sc->sc_node); 284 if (parent == 0) 285 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 286 287 if (sc->sc_rangeslen < 0) 288 return EINVAL; 289 if (sc->sc_rangeslen == 0) 290 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 291 292 rlen = sc->sc_rangeslen / sizeof(uint32_t); 293 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 294 295 /* For each range. */ 296 for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) { 297 /* Extract from and size, so we can see if we fit. */ 298 rfrom = range[0]; 299 if (sc->sc_acells == 2) 300 rfrom = (rfrom << 32) + range[1]; 301 rsize = range[sc->sc_acells + sc->sc_pacells]; 302 if (sc->sc_scells == 2) 303 rsize = (rsize << 32) + 304 range[sc->sc_acells + sc->sc_pacells + 1]; 305 306 /* Try next, if we're not in the range. */ 307 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 308 continue; 309 310 /* All good, extract to address and translate. */ 311 rto = range[sc->sc_acells]; 312 if (sc->sc_pacells == 2) 313 rto = (rto << 32) + range[sc->sc_acells + 1]; 314 315 addr -= rfrom; 316 addr += rto; 317 318 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 319 } 320 321 return ESRCH; 322 } 323 324 int 325 simplebus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 326 bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp, 327 int *segp, int first) 328 { 329 struct simplebus_softc *sc = t->_cookie; 330 int rlen, rone, seg; 331 int firstseg = *segp; 332 int error; 333 334 error = sc->sc_dmat->_dmamap_load_buffer(sc->sc_dmat, map, buf, buflen, 335 p, flags, lastaddrp, segp, first); 336 if (error) 337 return error; 338 339 if (sc->sc_dmaranges == NULL) 340 return 0; 341 342 rlen = sc->sc_dmarangeslen / sizeof(uint32_t); 343 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 344 345 /* For each segment. */ 346 for (seg = firstseg; seg <= *segp; seg++) { 347 uint64_t addr, size, rfrom, rto, rsize; 348 uint32_t *range; 349 350 addr = map->dm_segs[seg].ds_addr; 351 size = map->dm_segs[seg].ds_len; 352 353 /* For each range. */ 354 for (range = sc->sc_dmaranges; rlen >= rone; 355 rlen -= rone, range += rone) { 356 /* Extract from and size, so we can see if we fit. */ 357 rfrom = range[sc->sc_acells]; 358 if (sc->sc_pacells == 2) 359 rfrom = (rfrom << 32) + range[sc->sc_acells + 1]; 360 361 rsize = range[sc->sc_acells + sc->sc_pacells]; 362 if (sc->sc_scells == 2) 363 rsize = (rsize << 32) + 364 range[sc->sc_acells + sc->sc_pacells + 1]; 365 366 /* Try next, if we're not in the range. */ 367 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 368 continue; 369 370 /* All good, extract to address and translate. */ 371 rto = range[0]; 372 if (sc->sc_acells == 2) 373 rto = (rto << 32) + range[1]; 374 375 map->dm_segs[seg].ds_addr -= rfrom; 376 map->dm_segs[seg].ds_addr += rto; 377 break; 378 } 379 } 380 381 return 0; 382 } 383