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