1 /* $OpenBSD: simplebus.c,v 1.3 2023/09/22 01:10:43 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 <machine/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
simplebus_match(struct device * parent,void * cfdata,void * aux)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
simplebus_attach(struct device * parent,struct device * self,void * aux)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
simplebus_submatch(struct device * self,void * match,void * aux)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
simplebus_print(void * aux,const char * pnp)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
simplebus_attach_node(struct device * self,int node)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
simplebus_bs_map(bus_space_tag_t t,bus_addr_t bpa,bus_size_t size,int flag,bus_space_handle_t * bshp)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