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