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