1 /* $OpenBSD: simplebus.c,v 1.9 2016/09/18 17:50:26 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 <arm/fdt.h> 28 29 int simplebus_match(struct device *, void *, void *); 30 void simplebus_attach(struct device *, struct device *, void *); 31 32 void simplebus_attach_node(struct device *, int); 33 int simplebus_bs_map(void *, bus_addr_t, bus_size_t, int, bus_space_handle_t *); 34 35 struct simplebus_softc { 36 struct device sc_dev; 37 int sc_node; 38 bus_space_tag_t sc_iot; 39 bus_dma_tag_t sc_dmat; 40 int sc_acells; 41 int sc_scells; 42 int sc_pacells; 43 int sc_pscells; 44 struct bus_space sc_bus; 45 int *sc_ranges; 46 int sc_rangeslen; 47 int sc_early; 48 }; 49 50 struct cfattach simplebus_ca = { 51 sizeof(struct simplebus_softc), simplebus_match, simplebus_attach, NULL, 52 config_activate_children 53 }; 54 55 struct cfdriver simplebus_cd = { 56 NULL, "simplebus", DV_DULL 57 }; 58 59 /* 60 * Simplebus is a generic bus with no special casings. 61 */ 62 int 63 simplebus_match(struct device *parent, void *cfdata, void *aux) 64 { 65 struct fdt_attach_args *fa = (struct fdt_attach_args *)aux; 66 67 if (fa->fa_node == 0) 68 return (0); 69 70 if (!OF_is_compatible(fa->fa_node, "simple-bus")) 71 return (0); 72 73 return (1); 74 } 75 76 void 77 simplebus_attach(struct device *parent, struct device *self, void *aux) 78 { 79 struct simplebus_softc *sc = (struct simplebus_softc *)self; 80 struct fdt_attach_args *fa = (struct fdt_attach_args *)aux; 81 char name[32]; 82 int node; 83 84 sc->sc_node = fa->fa_node; 85 sc->sc_iot = fa->fa_iot; 86 sc->sc_dmat = fa->fa_dmat; 87 sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells", 88 fa->fa_acells); 89 sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells", 90 fa->fa_scells); 91 sc->sc_pacells = fa->fa_acells; 92 sc->sc_pscells = fa->fa_scells; 93 94 if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) { 95 name[sizeof(name) - 1] = 0; 96 printf(": \"%s\"", name); 97 } 98 99 printf("\n"); 100 101 memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus)); 102 sc->sc_bus.bs_cookie = sc; 103 sc->sc_bus.bs_map = simplebus_bs_map; 104 105 sc->sc_rangeslen = OF_getproplen(sc->sc_node, "ranges"); 106 if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) { 107 sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK); 108 OF_getpropintarray(sc->sc_node, "ranges", sc->sc_ranges, 109 sc->sc_rangeslen); 110 } 111 112 /* Scan the whole tree. */ 113 sc->sc_early = 1; 114 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 115 simplebus_attach_node(self, node); 116 117 sc->sc_early = 0; 118 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) 119 simplebus_attach_node(self, node); 120 } 121 122 int 123 simplebus_submatch(struct device *self, void *match, void *aux) 124 { 125 struct simplebus_softc *sc = (struct simplebus_softc *)self; 126 struct cfdata *cf = match; 127 128 if (cf->cf_loc[0] == sc->sc_early) 129 return (*cf->cf_attach->ca_match)(self, match, aux); 130 return 0; 131 } 132 133 /* 134 * Look for a driver that wants to be attached to this node. 135 */ 136 void 137 simplebus_attach_node(struct device *self, int node) 138 { 139 struct simplebus_softc *sc = (struct simplebus_softc *)self; 140 struct fdt_attach_args fa; 141 char buffer[128]; 142 int i, len, line; 143 uint32_t *cell, *reg; 144 145 if (!OF_getprop(node, "compatible", buffer, sizeof(buffer))) 146 return; 147 148 if (OF_getprop(node, "status", buffer, sizeof(buffer))) 149 if (!strcmp(buffer, "disabled")) 150 return; 151 152 memset(&fa, 0, sizeof(fa)); 153 fa.fa_name = ""; 154 fa.fa_node = node; 155 fa.fa_iot = &sc->sc_bus; 156 fa.fa_dmat = sc->sc_dmat; 157 fa.fa_acells = sc->sc_acells; 158 fa.fa_scells = sc->sc_scells; 159 160 len = OF_getproplen(node, "reg"); 161 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t); 162 if (len > 0 && line > 0 && (len % line) == 0) { 163 reg = malloc(len, M_TEMP, M_WAITOK); 164 OF_getpropintarray(node, "reg", reg, len); 165 166 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg), 167 M_DEVBUF, M_WAITOK | M_ZERO); 168 fa.fa_nreg = (len / line); 169 170 for (i = 0, cell = reg; i < len / line; i++) { 171 if (sc->sc_acells >= 1) 172 fa.fa_reg[i].addr = cell[0]; 173 if (sc->sc_acells == 2) { 174 fa.fa_reg[i].addr <<= 32; 175 fa.fa_reg[i].addr |= cell[1]; 176 } 177 cell += sc->sc_acells; 178 if (sc->sc_scells >= 1) 179 fa.fa_reg[i].size = cell[0]; 180 if (sc->sc_scells == 2) { 181 fa.fa_reg[i].size <<= 32; 182 fa.fa_reg[i].size |= cell[1]; 183 } 184 cell += sc->sc_scells; 185 } 186 187 free(reg, M_TEMP, len); 188 } 189 190 len = OF_getproplen(node, "interrupts"); 191 if (len > 0 && (len % sizeof(uint32_t)) == 0) { 192 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK); 193 fa.fa_nintr = len / sizeof(uint32_t); 194 195 OF_getpropintarray(node, "interrupts", fa.fa_intr, len); 196 } 197 198 config_found_sm(self, &fa, NULL, simplebus_submatch); 199 200 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg)); 201 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t)); 202 } 203 204 /* 205 * Translate memory address if needed. 206 */ 207 int 208 simplebus_bs_map(void *t, bus_addr_t bpa, bus_size_t size, 209 int flag, bus_space_handle_t *bshp) 210 { 211 struct simplebus_softc *sc = (struct simplebus_softc *)t; 212 uint64_t addr, rfrom, rto, rsize; 213 uint32_t *range; 214 int parent, rlen, rone; 215 216 addr = bpa; 217 parent = OF_parent(sc->sc_node); 218 if (parent == 0) 219 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 220 221 if (sc->sc_rangeslen < 0) 222 return EINVAL; 223 if (sc->sc_rangeslen == 0) 224 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 225 226 rlen = sc->sc_rangeslen / sizeof(uint32_t); 227 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells; 228 229 /* For each range. */ 230 for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) { 231 /* Extract from and size, so we can see if we fit. */ 232 rfrom = range[0]; 233 if (sc->sc_acells == 2) 234 rfrom = (rfrom << 32) + range[1]; 235 rsize = range[sc->sc_acells + sc->sc_pacells]; 236 if (sc->sc_scells == 2) 237 rsize = (rsize << 32) + 238 range[sc->sc_acells + sc->sc_pacells + 1]; 239 240 /* Try next, if we're not in the range. */ 241 if (addr < rfrom || (addr + size) > (rfrom + rsize)) 242 continue; 243 244 /* All good, extract to address and translate. */ 245 rto = range[sc->sc_acells]; 246 if (sc->sc_pacells == 2) 247 rto = (rto << 32) + range[sc->sc_acells + 1]; 248 249 addr -= rfrom; 250 addr += rto; 251 252 return bus_space_map(sc->sc_iot, addr, size, flag, bshp); 253 } 254 255 return ESRCH; 256 } 257