xref: /openbsd-src/sys/arch/arm64/dev/simplebus.c (revision 94673892b7b28179327a9d4cb100f53993b0bd92)
1 /* $OpenBSD: simplebus.c,v 1.18 2023/09/22 01:10:43 jsg 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 <machine/fdt.h>
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/fdt.h>
27 #include <dev/ofw/ofw_misc.h>
28 
29 #include <machine/fdt.h>
30 #include <machine/simplebusvar.h>
31 
32 int simplebus_match(struct device *, void *, void *);
33 void simplebus_attach(struct device *, struct device *, void *);
34 
35 void simplebus_attach_node(struct device *, int);
36 int simplebus_bs_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
37     bus_space_handle_t *);
38 paddr_t simplebus_bs_mmap(bus_space_tag_t, bus_addr_t, off_t, int, int);
39 int simplebus_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *,
40     bus_size_t, struct proc *, int, paddr_t *, int *, int);
41 int simplebus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t,
42     bus_dma_segment_t *, int, bus_size_t, int);
43 
44 const struct cfattach simplebus_ca = {
45 	sizeof(struct simplebus_softc), simplebus_match, simplebus_attach
46 };
47 
48 struct cfdriver simplebus_cd = {
49 	NULL, "simplebus", DV_DULL
50 };
51 
52 /*
53  * Simplebus is a generic bus with no special casings.
54  */
55 int
simplebus_match(struct device * parent,void * cfdata,void * aux)56 simplebus_match(struct device *parent, void *cfdata, void *aux)
57 {
58 	struct fdt_attach_args *fa = (struct fdt_attach_args *)aux;
59 
60 	if (fa->fa_node == 0)
61 		return (0);
62 
63 	/* Qualcomm GENI can mostly be treated as simple-bus. */
64 	if (OF_is_compatible(fa->fa_node, "qcom,geni-se-qup"))
65 		return (1);
66 
67 	if (!OF_is_compatible(fa->fa_node, "simple-bus"))
68 		return (0);
69 
70 	return (1);
71 }
72 
73 void
simplebus_attach(struct device * parent,struct device * self,void * aux)74 simplebus_attach(struct device *parent, struct device *self, void *aux)
75 {
76 	struct simplebus_softc *sc = (struct simplebus_softc *)self;
77 	struct fdt_attach_args *fa = (struct fdt_attach_args *)aux;
78 	char name[32];
79 	int node;
80 
81 	sc->sc_node = fa->fa_node;
82 	sc->sc_iot = fa->fa_iot;
83 	sc->sc_dmat = fa->fa_dmat;
84 	sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells",
85 	    fa->fa_acells);
86 	sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells",
87 	    fa->fa_scells);
88 	sc->sc_pacells = fa->fa_acells;
89 	sc->sc_pscells = fa->fa_scells;
90 
91 	if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) {
92 		name[sizeof(name) - 1] = 0;
93 		printf(": \"%s\"", name);
94 	}
95 
96 	printf("\n");
97 
98 	memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus));
99 	sc->sc_bus.bus_private = sc;
100 	sc->sc_bus._space_map = simplebus_bs_map;
101 	sc->sc_bus._space_mmap = simplebus_bs_mmap;
102 
103 	sc->sc_rangeslen = OF_getproplen(sc->sc_node, "ranges");
104 	if (sc->sc_rangeslen > 0 &&
105 	    (sc->sc_rangeslen % sizeof(uint32_t)) == 0) {
106 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
107 		OF_getpropintarray(sc->sc_node, "ranges", sc->sc_ranges,
108 		    sc->sc_rangeslen);
109 	}
110 
111 	memcpy(&sc->sc_dma, sc->sc_dmat, sizeof(sc->sc_dma));
112 	sc->sc_dma._dmamap_load_buffer = simplebus_dmamap_load_buffer;
113 	sc->sc_dma._dmamap_load_raw = simplebus_dmamap_load_raw;
114 	sc->sc_dma._cookie = sc;
115 
116 	sc->sc_dmarangeslen = OF_getproplen(sc->sc_node, "dma-ranges");
117 	if (sc->sc_dmarangeslen > 0 &&
118 	    (sc->sc_dmarangeslen % sizeof(uint32_t)) == 0) {
119 		sc->sc_dmaranges = malloc(sc->sc_dmarangeslen,
120 		    M_TEMP, M_WAITOK);
121 		OF_getpropintarray(sc->sc_node, "dma-ranges",
122 		    sc->sc_dmaranges, sc->sc_dmarangeslen);
123 	}
124 
125 	/*
126 	 * The device tree provided by the Raspberry Pi firmware lacks
127 	 * a "dma-ranges" option.  So provide the information until
128 	 * that gets fixed.
129 	 */
130 	if (sc->sc_dmaranges == NULL) {
131 		node = OF_parent(sc->sc_node);
132 		if (OF_is_compatible(node, "brcm,bcm2709")) {
133 			sc->sc_dmarangeslen = 3 * sizeof(uint32_t);
134 			sc->sc_dmaranges = malloc(sc->sc_dmarangeslen,
135 			    M_TEMP, M_WAITOK);
136 			sc->sc_dmaranges[0] = 0xc0000000;
137 			sc->sc_dmaranges[1] = 0x00000000;
138 			sc->sc_dmaranges[2] = 0x3f000000;
139 		}
140 	}
141 
142 	/* Scan the whole tree. */
143 	for (sc->sc_early = 2; sc->sc_early >= 0; sc->sc_early--) {
144 		for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
145 			simplebus_attach_node(self, node);
146 	}
147 }
148 
149 int
simplebus_submatch(struct device * self,void * match,void * aux)150 simplebus_submatch(struct device *self, void *match, void *aux)
151 {
152 	struct simplebus_softc	*sc = (struct simplebus_softc *)self;
153 	struct cfdata *cf = match;
154 
155 	if (cf->cf_loc[0] == sc->sc_early)
156 		return (*cf->cf_attach->ca_match)(self, match, aux);
157 	return 0;
158 }
159 
160 int
simplebus_print(void * aux,const char * pnp)161 simplebus_print(void *aux, const char *pnp)
162 {
163 	struct fdt_attach_args *fa = aux;
164 	char name[32];
165 
166 	if (!pnp)
167 		return (QUIET);
168 
169 	if (OF_getprop(fa->fa_node, "name", name, sizeof(name)) > 0) {
170 		name[sizeof(name) - 1] = 0;
171 		printf("\"%s\"", name);
172 	} else
173 		printf("node %u", fa->fa_node);
174 
175 	printf(" at %s", pnp);
176 
177 	return (UNCONF);
178 }
179 
180 /*
181  * Look for a driver that wants to be attached to this node.
182  */
183 void
simplebus_attach_node(struct device * self,int node)184 simplebus_attach_node(struct device *self, int node)
185 {
186 	struct simplebus_softc	*sc = (struct simplebus_softc *)self;
187 	struct fdt_attach_args	 fa;
188 	char			 buf[32];
189 	int			 i, len, line;
190 	uint32_t		*cell, *reg;
191 	struct device		*child;
192 
193 	if (OF_getproplen(node, "compatible") <= 0)
194 		return;
195 
196 	if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 &&
197 	    strcmp(buf, "disabled") == 0)
198 		return;
199 
200 	/* Skip if already attached early. */
201 	for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
202 		if (sc->sc_early_nodes[i] == node)
203 			return;
204 		if (sc->sc_early_nodes[i] == 0)
205 			break;
206 	}
207 
208 	memset(&fa, 0, sizeof(fa));
209 	fa.fa_name = "";
210 	fa.fa_node = node;
211 	fa.fa_iot = &sc->sc_bus;
212 	fa.fa_dmat = &sc->sc_dma;
213 	fa.fa_acells = sc->sc_acells;
214 	fa.fa_scells = sc->sc_scells;
215 
216 	len = OF_getproplen(node, "reg");
217 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
218 	if (len > 0 && line > 0 && (len % line) == 0) {
219 		reg = malloc(len, M_TEMP, M_WAITOK);
220 		OF_getpropintarray(node, "reg", reg, len);
221 
222 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
223 		    M_DEVBUF, M_WAITOK | M_ZERO);
224 		fa.fa_nreg = (len / line);
225 
226 		for (i = 0, cell = reg; i < len / line; i++) {
227 			if (sc->sc_acells >= 1)
228 				fa.fa_reg[i].addr = cell[0];
229 			if (sc->sc_acells == 2) {
230 				fa.fa_reg[i].addr <<= 32;
231 				fa.fa_reg[i].addr |= cell[1];
232 			}
233 			cell += sc->sc_acells;
234 			if (sc->sc_scells >= 1)
235 				fa.fa_reg[i].size = cell[0];
236 			if (sc->sc_scells == 2) {
237 				fa.fa_reg[i].size <<= 32;
238 				fa.fa_reg[i].size |= cell[1];
239 			}
240 			cell += sc->sc_scells;
241 		}
242 
243 		free(reg, M_TEMP, len);
244 	}
245 
246 	len = OF_getproplen(node, "interrupts");
247 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
248 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
249 		fa.fa_nintr = len / sizeof(uint32_t);
250 
251 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
252 	}
253 
254 	if (OF_getproplen(node, "dma-coherent") >= 0) {
255 		fa.fa_dmat = malloc(sizeof(sc->sc_dma),
256 		    M_DEVBUF, M_WAITOK | M_ZERO);
257 		memcpy(fa.fa_dmat, &sc->sc_dma, sizeof(sc->sc_dma));
258 		fa.fa_dmat->_flags |= BUS_DMA_COHERENT;
259 	}
260 
261 	fa.fa_dmat = iommu_device_map(fa.fa_node, fa.fa_dmat);
262 
263 	child = config_found_sm(self, &fa, sc->sc_early ? NULL :
264 	    simplebus_print, simplebus_submatch);
265 
266 	/* Record nodes that we attach early. */
267 	if (child && sc->sc_early) {
268 		for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
269 			if (sc->sc_early_nodes[i] != 0)
270 				continue;
271 			sc->sc_early_nodes[i] = node;
272 			break;
273 		}
274 	}
275 
276 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
277 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
278 }
279 
280 /*
281  * Translate memory address if needed.
282  */
283 int
simplebus_bs_map(bus_space_tag_t t,bus_addr_t bpa,bus_size_t size,int flag,bus_space_handle_t * bshp)284 simplebus_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size,
285     int flag, bus_space_handle_t *bshp)
286 {
287 	struct simplebus_softc *sc = t->bus_private;
288 	uint64_t addr, rfrom, rto, rsize;
289 	uint32_t *range;
290 	int parent, rlen, rone;
291 
292 	addr = bpa;
293 	parent = OF_parent(sc->sc_node);
294 	if (parent == 0)
295 		return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
296 
297 	if (sc->sc_rangeslen < 0)
298 		return EINVAL;
299 	if (sc->sc_rangeslen == 0)
300 		return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
301 
302 	rlen = sc->sc_rangeslen / sizeof(uint32_t);
303 	rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
304 
305 	/* For each range. */
306 	for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) {
307 		/* Extract from and size, so we can see if we fit. */
308 		rfrom = range[0];
309 		if (sc->sc_acells == 2)
310 			rfrom = (rfrom << 32) + range[1];
311 		rsize = range[sc->sc_acells + sc->sc_pacells];
312 		if (sc->sc_scells == 2)
313 			rsize = (rsize << 32) +
314 			    range[sc->sc_acells + sc->sc_pacells + 1];
315 
316 		/* Try next, if we're not in the range. */
317 		if (addr < rfrom || (addr + size) > (rfrom + rsize))
318 			continue;
319 
320 		/* All good, extract to address and translate. */
321 		rto = range[sc->sc_acells];
322 		if (sc->sc_pacells == 2)
323 			rto = (rto << 32) + range[sc->sc_acells + 1];
324 
325 		addr -= rfrom;
326 		addr += rto;
327 
328 		return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
329 	}
330 
331 	return ESRCH;
332 }
333 
334 paddr_t
simplebus_bs_mmap(bus_space_tag_t t,bus_addr_t bpa,off_t off,int prot,int flags)335 simplebus_bs_mmap(bus_space_tag_t t, bus_addr_t bpa, off_t off,
336     int prot, int flags)
337 {
338 	struct simplebus_softc *sc = t->bus_private;
339 	uint64_t addr, rfrom, rto, rsize;
340 	uint32_t *range;
341 	int parent, rlen, rone;
342 
343 	addr = bpa;
344 	parent = OF_parent(sc->sc_node);
345 	if (parent == 0)
346 		return bus_space_mmap(sc->sc_iot, addr, off, prot, flags);
347 
348 	if (sc->sc_rangeslen < 0)
349 		return EINVAL;
350 	if (sc->sc_rangeslen == 0)
351 		return bus_space_mmap(sc->sc_iot, addr, off, prot, flags);
352 
353 	rlen = sc->sc_rangeslen / sizeof(uint32_t);
354 	rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
355 
356 	/* For each range. */
357 	for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) {
358 		/* Extract from and size, so we can see if we fit. */
359 		rfrom = range[0];
360 		if (sc->sc_acells == 2)
361 			rfrom = (rfrom << 32) + range[1];
362 		rsize = range[sc->sc_acells + sc->sc_pacells];
363 		if (sc->sc_scells == 2)
364 			rsize = (rsize << 32) +
365 			    range[sc->sc_acells + sc->sc_pacells + 1];
366 
367 		/* Try next, if we're not in the range. */
368 		if (addr < rfrom || addr >= (rfrom + rsize))
369 			continue;
370 
371 		/* All good, extract to address and translate. */
372 		rto = range[sc->sc_acells];
373 		if (sc->sc_pacells == 2)
374 			rto = (rto << 32) + range[sc->sc_acells + 1];
375 
376 		addr -= rfrom;
377 		addr += rto;
378 
379 		return bus_space_mmap(sc->sc_iot, addr, off, prot, flags);
380 	}
381 
382 	return -1;
383 }
384 
385 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)386 simplebus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
387     bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp,
388     int *segp, int first)
389 {
390 	struct simplebus_softc *sc = t->_cookie;
391 	int rlen, rone, seg;
392 	int firstseg = *segp;
393 	int error;
394 
395 	error = sc->sc_dmat->_dmamap_load_buffer(sc->sc_dmat, map, buf, buflen,
396 	    p, flags, lastaddrp, segp, first);
397 	if (error)
398 		return error;
399 
400 	if (sc->sc_dmaranges == NULL)
401 		return 0;
402 
403 	rlen = sc->sc_dmarangeslen / sizeof(uint32_t);
404 	rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
405 
406 	/* For each segment. */
407 	for (seg = firstseg; seg <= *segp; seg++) {
408 		uint64_t addr, size, rfrom, rto, rsize;
409 		uint32_t *range;
410 
411 		addr = map->dm_segs[seg].ds_addr;
412 		size = map->dm_segs[seg].ds_len;
413 
414 		/* For each range. */
415 		for (range = sc->sc_dmaranges; rlen >= rone;
416 		     rlen -= rone, range += rone) {
417 			/* Extract from and size, so we can see if we fit. */
418 			rfrom = range[sc->sc_acells];
419 			if (sc->sc_pacells == 2)
420 				rfrom = (rfrom << 32) + range[sc->sc_acells + 1];
421 
422 			rsize = range[sc->sc_acells + sc->sc_pacells];
423 			if (sc->sc_scells == 2)
424 				rsize = (rsize << 32) +
425 				    range[sc->sc_acells + sc->sc_pacells + 1];
426 
427 			/* Try next, if we're not in the range. */
428 			if (addr < rfrom || (addr + size) > (rfrom + rsize))
429 				continue;
430 
431 			/* All good, extract to address and translate. */
432 			rto = range[0];
433 			if (sc->sc_acells == 2)
434 				rto = (rto << 32) + range[1];
435 
436 			map->dm_segs[seg].ds_addr -= rfrom;
437 			map->dm_segs[seg].ds_addr += rto;
438 			break;
439 		}
440 	}
441 
442 	return 0;
443 }
444 
445 int
simplebus_dmamap_load_raw(bus_dma_tag_t t,bus_dmamap_t map,bus_dma_segment_t * segs,int nsegs,bus_size_t size,int flags)446 simplebus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
447     bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
448 {
449 	struct simplebus_softc *sc = t->_cookie;
450 	int rlen, rone, seg;
451 	int error;
452 
453 	error = sc->sc_dmat->_dmamap_load_raw(sc->sc_dmat, map,
454 	     segs, nsegs, size, flags);
455 	if (error)
456 		return error;
457 
458 	if (sc->sc_dmaranges == NULL)
459 		return 0;
460 
461 	rlen = sc->sc_dmarangeslen / sizeof(uint32_t);
462 	rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
463 
464 	/* For each segment. */
465 	for (seg = 0; seg < map->dm_nsegs; seg++) {
466 		uint64_t addr, size, rfrom, rto, rsize;
467 		uint32_t *range;
468 
469 		addr = map->dm_segs[seg].ds_addr;
470 		size = map->dm_segs[seg].ds_len;
471 
472 		/* For each range. */
473 		for (range = sc->sc_dmaranges; rlen >= rone;
474 		     rlen -= rone, range += rone) {
475 			/* Extract from and size, so we can see if we fit. */
476 			rfrom = range[sc->sc_acells];
477 			if (sc->sc_pacells == 2)
478 				rfrom = (rfrom << 32) + range[sc->sc_acells + 1];
479 
480 			rsize = range[sc->sc_acells + sc->sc_pacells];
481 			if (sc->sc_scells == 2)
482 				rsize = (rsize << 32) +
483 				    range[sc->sc_acells + sc->sc_pacells + 1];
484 
485 			/* Try next, if we're not in the range. */
486 			if (addr < rfrom || (addr + size) > (rfrom + rsize))
487 				continue;
488 
489 			/* All good, extract to address and translate. */
490 			rto = range[0];
491 			if (sc->sc_acells == 2)
492 				rto = (rto << 32) + range[1];
493 
494 			map->dm_segs[seg].ds_addr -= rfrom;
495 			map->dm_segs[seg].ds_addr += rto;
496 			break;
497 		}
498 	}
499 
500 	return 0;
501 }
502