1 /* $OpenBSD: simplebus.c,v 1.7 2024/04/07 21:08:59 kettenis 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/device.h>
22 #include <sys/malloc.h>
23
24 #include <dev/ofw/openfirm.h>
25 #include <dev/ofw/fdt.h>
26
27 #include <machine/fdt.h>
28 #include <machine/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 const 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
simplebus_match(struct device * parent,void * cfdata,void * aux)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
simplebus_attach(struct device * parent,struct device * self,void * aux)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 /* Scan the whole tree. */
115 sc->sc_early = 1;
116 for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
117 simplebus_attach_node(self, node);
118
119 sc->sc_early = 0;
120 for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
121 simplebus_attach_node(self, node);
122 }
123
124 int
simplebus_submatch(struct device * self,void * match,void * aux)125 simplebus_submatch(struct device *self, void *match, void *aux)
126 {
127 struct simplebus_softc *sc = (struct simplebus_softc *)self;
128 struct cfdata *cf = match;
129
130 if (cf->cf_loc[0] == sc->sc_early)
131 return (*cf->cf_attach->ca_match)(self, match, aux);
132 return 0;
133 }
134
135 int
simplebus_print(void * aux,const char * pnp)136 simplebus_print(void *aux, const char *pnp)
137 {
138 struct fdt_attach_args *fa = aux;
139 char name[32];
140
141 if (!pnp)
142 return (QUIET);
143
144 if (OF_getprop(fa->fa_node, "name", name, sizeof(name)) > 0) {
145 name[sizeof(name) - 1] = 0;
146 printf("\"%s\"", name);
147 } else
148 printf("node %u", fa->fa_node);
149
150 printf(" at %s", pnp);
151
152 return (UNCONF);
153 }
154
155 /*
156 * Look for a driver that wants to be attached to this node.
157 */
158 void
simplebus_attach_node(struct device * self,int node)159 simplebus_attach_node(struct device *self, int node)
160 {
161 struct simplebus_softc *sc = (struct simplebus_softc *)self;
162 struct fdt_attach_args fa;
163 char buf[32];
164 int i, len, line;
165 uint32_t *cell, *reg;
166 struct device *child;
167
168 if (OF_getproplen(node, "compatible") <= 0)
169 return;
170
171 if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 &&
172 strcmp(buf, "disabled") == 0)
173 return;
174
175 /* Skip if already attached early. */
176 for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
177 if (sc->sc_early_nodes[i] == node)
178 return;
179 if (sc->sc_early_nodes[i] == 0)
180 break;
181 }
182
183 memset(&fa, 0, sizeof(fa));
184 fa.fa_name = "";
185 fa.fa_node = node;
186 fa.fa_iot = &sc->sc_bus;
187 fa.fa_dmat = &sc->sc_dma;
188 fa.fa_acells = sc->sc_acells;
189 fa.fa_scells = sc->sc_scells;
190
191 len = OF_getproplen(node, "reg");
192 line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
193 if (len > 0 && line > 0 && (len % line) == 0) {
194 reg = malloc(len, M_TEMP, M_WAITOK);
195 OF_getpropintarray(node, "reg", reg, len);
196
197 fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
198 M_DEVBUF, M_WAITOK | M_ZERO);
199 fa.fa_nreg = (len / line);
200
201 for (i = 0, cell = reg; i < len / line; i++) {
202 if (sc->sc_acells >= 1)
203 fa.fa_reg[i].addr = cell[0];
204 if (sc->sc_acells == 2) {
205 fa.fa_reg[i].addr <<= 32;
206 fa.fa_reg[i].addr |= cell[1];
207 }
208 cell += sc->sc_acells;
209 if (sc->sc_scells >= 1)
210 fa.fa_reg[i].size = cell[0];
211 if (sc->sc_scells == 2) {
212 fa.fa_reg[i].size <<= 32;
213 fa.fa_reg[i].size |= cell[1];
214 }
215 cell += sc->sc_scells;
216 }
217
218 free(reg, M_TEMP, len);
219 }
220
221 len = OF_getproplen(node, "interrupts");
222 if (len > 0 && (len % sizeof(uint32_t)) == 0) {
223 fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
224 fa.fa_nintr = len / sizeof(uint32_t);
225
226 OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
227 }
228
229 if (OF_getproplen(node, "dma-noncoherent") >= 0) {
230 fa.fa_dmat = malloc(sizeof(sc->sc_dma),
231 M_DEVBUF, M_WAITOK | M_ZERO);
232 memcpy(fa.fa_dmat, &sc->sc_dma, sizeof(sc->sc_dma));
233 fa.fa_dmat->_flags &= ~BUS_DMA_COHERENT;
234 } else if (OF_getproplen(node, "dma-coherent") >= 0) {
235 fa.fa_dmat = malloc(sizeof(sc->sc_dma),
236 M_DEVBUF, M_WAITOK | M_ZERO);
237 memcpy(fa.fa_dmat, &sc->sc_dma, sizeof(sc->sc_dma));
238 fa.fa_dmat->_flags |= BUS_DMA_COHERENT;
239 }
240
241 #ifdef DEBUG_AUTOCONF
242 if (OF_getprop(fa.fa_node, "name", buf, sizeof(buf)) > 0)
243 printf("\ncurrent parent: %s, current node: %d-%s\n", self->dv_xname, fa.fa_node, buf);
244 #endif
245
246 child = config_found_sm(self, &fa, sc->sc_early ? NULL :
247 simplebus_print, simplebus_submatch);
248
249 /* Record nodes that we attach early. */
250 if (child && sc->sc_early) {
251 for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
252 if (sc->sc_early_nodes[i] != 0)
253 continue;
254 sc->sc_early_nodes[i] = node;
255 break;
256 }
257 }
258
259 free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
260 free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
261 }
262
263 /*
264 * Translate memory address if needed.
265 */
266 int
simplebus_bs_map(bus_space_tag_t t,bus_addr_t bpa,bus_size_t size,int flag,bus_space_handle_t * bshp)267 simplebus_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size,
268 int flag, bus_space_handle_t *bshp)
269 {
270 struct simplebus_softc *sc = t->bus_private;
271 uint64_t addr, rfrom, rto, rsize;
272 uint32_t *range;
273 int parent, rlen, rone;
274
275 addr = bpa;
276 parent = OF_parent(sc->sc_node);
277 if (parent == 0)
278 return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
279
280 if (sc->sc_rangeslen < 0)
281 return EINVAL;
282 if (sc->sc_rangeslen == 0)
283 return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
284
285 rlen = sc->sc_rangeslen / sizeof(uint32_t);
286 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
287
288 /* For each range. */
289 for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) {
290 /* Extract from and size, so we can see if we fit. */
291 rfrom = range[0];
292 if (sc->sc_acells == 2)
293 rfrom = (rfrom << 32) + range[1];
294 rsize = range[sc->sc_acells + sc->sc_pacells];
295 if (sc->sc_scells == 2)
296 rsize = (rsize << 32) +
297 range[sc->sc_acells + sc->sc_pacells + 1];
298
299 /* Try next, if we're not in the range. */
300 if (addr < rfrom || (addr + size) > (rfrom + rsize))
301 continue;
302
303 /* All good, extract to address and translate. */
304 rto = range[sc->sc_acells];
305 if (sc->sc_pacells == 2)
306 rto = (rto << 32) + range[sc->sc_acells + 1];
307
308 addr -= rfrom;
309 addr += rto;
310
311 return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
312 }
313
314 return ESRCH;
315 }
316
317 int
simplebus_dmamap_load_buffer(bus_dma_tag_t t,bus_dmamap_t map,void * buf,bus_size_t buflen,struct proc * p,int flags,paddr_t * lastaddrp,int * segp,int first)318 simplebus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
319 bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp,
320 int *segp, int first)
321 {
322 struct simplebus_softc *sc = t->_cookie;
323 int rlen, rone, seg;
324 int firstseg = *segp;
325 int error;
326
327 error = sc->sc_dmat->_dmamap_load_buffer(sc->sc_dmat, map, buf, buflen,
328 p, flags, lastaddrp, segp, first);
329 if (error)
330 return error;
331
332 if (sc->sc_dmaranges == NULL)
333 return 0;
334
335 rlen = sc->sc_dmarangeslen / sizeof(uint32_t);
336 rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
337
338 /* For each segment. */
339 for (seg = firstseg; seg <= *segp; seg++) {
340 uint64_t addr, size, rfrom, rto, rsize;
341 uint32_t *range;
342
343 addr = map->dm_segs[seg].ds_addr;
344 size = map->dm_segs[seg].ds_len;
345
346 /* For each range. */
347 for (range = sc->sc_dmaranges; rlen >= rone;
348 rlen -= rone, range += rone) {
349 /* Extract from and size, so we can see if we fit. */
350 rfrom = range[sc->sc_acells];
351 if (sc->sc_pacells == 2)
352 rfrom = (rfrom << 32) + range[sc->sc_acells + 1];
353
354 rsize = range[sc->sc_acells + sc->sc_pacells];
355 if (sc->sc_scells == 2)
356 rsize = (rsize << 32) +
357 range[sc->sc_acells + sc->sc_pacells + 1];
358
359 /* Try next, if we're not in the range. */
360 if (addr < rfrom || (addr + size) > (rfrom + rsize))
361 continue;
362
363 /* All good, extract to address and translate. */
364 rto = range[0];
365 if (sc->sc_acells == 2)
366 rto = (rto << 32) + range[1];
367
368 map->dm_segs[seg].ds_addr -= rfrom;
369 map->dm_segs[seg].ds_addr += rto;
370 break;
371 }
372 }
373
374 return 0;
375 }
376