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
32int simplebus_match(struct device *, void *, void *);
33void simplebus_attach(struct device *, struct device *, void *);
34
35void simplebus_attach_node(struct device *, int);
36int simplebus_bs_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
37    bus_space_handle_t *);
38paddr_t simplebus_bs_mmap(bus_space_tag_t, bus_addr_t, off_t, int, int);
39int simplebus_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *,
40    bus_size_t, struct proc *, int, paddr_t *, int *, int);
41int simplebus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t,
42    bus_dma_segment_t *, int, bus_size_t, int);
43
44const struct cfattach simplebus_ca = {
45	sizeof(struct simplebus_softc), simplebus_match, simplebus_attach
46};
47
48struct cfdriver simplebus_cd = {
49	NULL, "simplebus", DV_DULL
50};
51
52/*
53 * Simplebus is a generic bus with no special casings.
54 */
55int
56simplebus_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
73void
74simplebus_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
149int
150simplebus_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
160int
161simplebus_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 */
183void
184simplebus_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 */
283int
284simplebus_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
334paddr_t
335simplebus_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
385int
386simplebus_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
445int
446simplebus_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