1 /* $OpenBSD: simplebus.c,v 1.8 2017/06/01 21:19:07 patrick 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 /* 153 * Look for a driver that wants to be attached to this node. 154 */ 155 void 156 simplebus_attach_node(struct device *self, int node) 157 { 158 struct simplebus_softc *sc = (struct simplebus_softc *)self; 159 struct fdt_attach_args fa; 160 char buf[32]; 161 int i, len, line; 162 uint32_t *cell, *reg; 163 164 if (OF_getproplen(node, "compatible") <= 0) 165 return; 166 167 if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && 168 strcmp(buf, "disabled") == 0) 169 return; 170 171 memset(&fa, 0, sizeof(fa)); 172 fa.fa_name = ""; 173 fa.fa_node = node; 174 fa.fa_iot = &sc->sc_bus; 175 fa.fa_dmat = &sc->sc_dma; 176 fa.fa_acells = sc->sc_acells; 177 fa.fa_scells = sc->sc_scells; 178 179 len = OF_getproplen(node, "reg"); 180 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 181 if (len > 0 && line > 0 && (len % line) == 0) { 182 reg = malloc(len, M_TEMP, M_WAITOK); 183 OF_getpropintarray(node, "reg", reg, len); 184 185 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 186 M_DEVBUF, M_WAITOK | M_ZERO); 187 fa.fa_nreg = (len / line); 188 189 for (i = 0, cell = reg; i < len / line; i++) { 190 if (sc->sc_acells >= 1) 191 fa.fa_reg[i].addr = cell[0]; 192 if (sc->sc_acells == 2) { 193 fa.fa_reg[i].addr <<= 32; 194 fa.fa_reg[i].addr |= cell[1]; 195 } 196 cell += sc->sc_acells; 197 if (sc->sc_scells >= 1) 198 fa.fa_reg[i].size = cell[0]; 199 if (sc->sc_scells == 2) { 200 fa.fa_reg[i].size <<= 32; 201 fa.fa_reg[i].size |= cell[1]; 202 } 203 cell += sc->sc_scells; 204 } 205 206 free(reg, M_TEMP, len); 207 } 208 209 len = OF_getproplen(node, "interrupts"); 210 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 211 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 212 fa.fa_nintr = len / sizeof(uint32_t); 213 214 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 215 } 216 217 if (OF_getproplen(node, "dma-coherent") >= 0) { 218 fa.fa_dmat = malloc(sizeof(sc->sc_dma), 219 M_DEVBUF, M_WAITOK | M_ZERO); 220 memcpy(fa.fa_dmat, &sc->sc_dma, sizeof(sc->sc_dma)); 221 fa.fa_dmat->_flags |= BUS_DMA_COHERENT; 222 } 223 224 config_found_sm(self, &fa, NULL, simplebus_submatch); 225 226 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 227 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 228 } 229 230 /* 231 * Translate memory address if needed. 232 */ 233 int 234 simplebus_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, 235 int flag, bus_space_handle_t *bshp) 236 { 237 struct simplebus_softc *sc = t->bus_private; 238 uint64_t addr, rfrom, rto, rsize; 239 uint32_t *range; 240 int parent, rlen, rone; 241 242 addr = bpa; 243 parent = OF_parent(sc->sc_node); 244 if (parent == 0) 245 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 246 247 if (sc->sc_rangeslen < 0) 248 return EINVAL; 249 if (sc->sc_rangeslen == 0) 250 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 251 252 rlen = sc->sc_rangeslen / sizeof(uint32_t); 253 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 254 255 /* For each range. */ 256 for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) { 257 /* Extract from and size, so we can see if we fit. */ 258 rfrom = range[0]; 259 if (sc->sc_acells == 2) 260 rfrom = (rfrom << 32) + range[1]; 261 rsize = range[sc->sc_acells + sc->sc_pacells]; 262 if (sc->sc_scells == 2) 263 rsize = (rsize << 32) + 264 range[sc->sc_acells + sc->sc_pacells + 1]; 265 266 /* Try next, if we're not in the range. */ 267 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 268 continue; 269 270 /* All good, extract to address and translate. */ 271 rto = range[sc->sc_acells]; 272 if (sc->sc_pacells == 2) 273 rto = (rto << 32) + range[sc->sc_acells + 1]; 274 275 addr -= rfrom; 276 addr += rto; 277 278 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 279 } 280 281 return ESRCH; 282 } 283 284 int 285 simplebus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 286 bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp, 287 int *segp, int first) 288 { 289 struct simplebus_softc *sc = t->_cookie; 290 int rlen, rone, seg; 291 int firstseg = *segp; 292 int error; 293 294 error = sc->sc_dmat->_dmamap_load_buffer(sc->sc_dmat, map, buf, buflen, 295 p, flags, lastaddrp, segp, first); 296 if (error) 297 return error; 298 299 if (sc->sc_dmaranges == NULL) 300 return 0; 301 302 rlen = sc->sc_dmarangeslen / sizeof(uint32_t); 303 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 304 305 /* For each segment. */ 306 for (seg = firstseg; seg <= *segp; seg++) { 307 uint64_t addr, size, rfrom, rto, rsize; 308 uint32_t *range; 309 310 addr = map->dm_segs[seg].ds_addr; 311 size = map->dm_segs[seg].ds_len; 312 313 /* For each range. */ 314 for (range = sc->sc_dmaranges; rlen >= rone; 315 rlen -= rone, range += rone) { 316 /* Extract from and size, so we can see if we fit. */ 317 rfrom = range[sc->sc_acells]; 318 if (sc->sc_pacells == 2) 319 rfrom = (rfrom << 32) + range[sc->sc_acells + 1]; 320 321 rsize = range[sc->sc_acells + sc->sc_pacells]; 322 if (sc->sc_scells == 2) 323 rsize = (rsize << 32) + 324 range[sc->sc_acells + sc->sc_pacells + 1]; 325 326 /* Try next, if we're not in the range. */ 327 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 328 continue; 329 330 /* All good, extract to address and translate. */ 331 rto = range[0]; 332 if (sc->sc_pacells == 2) 333 rto = (rto << 32) + range[1]; 334 335 map->dm_segs[seg].ds_addr -= rfrom; 336 map->dm_segs[seg].ds_addr += rto; 337 break; 338 } 339 } 340 341 return 0; 342 } 343