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