1*94673892Sjsg /* $OpenBSD: simplebus.c,v 1.3 2023/09/22 01:10:43 jsg Exp $ */
2c441d705Svisa
3c441d705Svisa /*
4c441d705Svisa * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se>
5c441d705Svisa *
6c441d705Svisa * Permission to use, copy, modify, and distribute this software for any
7c441d705Svisa * purpose with or without fee is hereby granted, provided that the above
8c441d705Svisa * copyright notice and this permission notice appear in all copies.
9c441d705Svisa *
10c441d705Svisa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c441d705Svisa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c441d705Svisa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c441d705Svisa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c441d705Svisa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c441d705Svisa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c441d705Svisa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c441d705Svisa */
18c441d705Svisa
19c441d705Svisa #include <sys/param.h>
20c441d705Svisa #include <sys/systm.h>
21c441d705Svisa #include <sys/kernel.h>
22c441d705Svisa #include <sys/device.h>
23c441d705Svisa #include <sys/malloc.h>
24c441d705Svisa
25c441d705Svisa #include <dev/ofw/openfirm.h>
26c441d705Svisa #include <dev/ofw/fdt.h>
27c441d705Svisa
28c441d705Svisa #include <machine/fdt.h>
29*94673892Sjsg #include <machine/simplebusvar.h>
30c441d705Svisa
31c441d705Svisa int simplebus_match(struct device *, void *, void *);
32c441d705Svisa void simplebus_attach(struct device *, struct device *, void *);
33c441d705Svisa
34c441d705Svisa void simplebus_attach_node(struct device *, int);
35c441d705Svisa int simplebus_bs_map(bus_space_tag_t, bus_addr_t, bus_size_t,
36c441d705Svisa int, bus_space_handle_t *);
37c441d705Svisa
38c441d705Svisa const struct cfattach simplebus_ca = {
39c441d705Svisa sizeof(struct simplebus_softc), simplebus_match, simplebus_attach
40c441d705Svisa };
41c441d705Svisa
42c441d705Svisa struct cfdriver simplebus_cd = {
43c441d705Svisa NULL, "simplebus", DV_DULL
44c441d705Svisa };
45c441d705Svisa
46c441d705Svisa /*
47c441d705Svisa * Simplebus is a generic bus with no special casings.
48c441d705Svisa */
49c441d705Svisa int
simplebus_match(struct device * parent,void * cfdata,void * aux)50c441d705Svisa simplebus_match(struct device *parent, void *cfdata, void *aux)
51c441d705Svisa {
52c441d705Svisa struct fdt_attach_args *fa = (struct fdt_attach_args *)aux;
53c441d705Svisa
54c441d705Svisa /* Guard against non-fdt attach on iobus(4). */
55c441d705Svisa if (strlen(fa->fa_name) > 0)
56c441d705Svisa return (0);
57c441d705Svisa
58c441d705Svisa if (fa->fa_node == 0)
59c441d705Svisa return (0);
60c441d705Svisa
61c441d705Svisa if (!OF_is_compatible(fa->fa_node, "simple-bus"))
62c441d705Svisa return (0);
63c441d705Svisa
64c441d705Svisa return (1);
65c441d705Svisa }
66c441d705Svisa
67c441d705Svisa void
simplebus_attach(struct device * parent,struct device * self,void * aux)68c441d705Svisa simplebus_attach(struct device *parent, struct device *self, void *aux)
69c441d705Svisa {
70c441d705Svisa struct simplebus_softc *sc = (struct simplebus_softc *)self;
71c441d705Svisa struct fdt_attach_args *fa = (struct fdt_attach_args *)aux;
72c441d705Svisa char name[32];
73c441d705Svisa int node;
74c441d705Svisa
75c441d705Svisa sc->sc_node = fa->fa_node;
76c441d705Svisa sc->sc_iot = fa->fa_iot;
77c441d705Svisa sc->sc_dmat = fa->fa_dmat;
78c441d705Svisa sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells",
79c441d705Svisa fa->fa_acells);
80c441d705Svisa sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells",
81c441d705Svisa fa->fa_scells);
82c441d705Svisa sc->sc_pacells = fa->fa_acells;
83c441d705Svisa sc->sc_pscells = fa->fa_scells;
84c441d705Svisa
85c441d705Svisa if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) {
86c441d705Svisa name[sizeof(name) - 1] = 0;
87c441d705Svisa printf(": \"%s\"", name);
88c441d705Svisa }
89c441d705Svisa
90c441d705Svisa printf("\n");
91c441d705Svisa
92c441d705Svisa memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus));
93c441d705Svisa sc->sc_bus.bus_private = sc;
94c441d705Svisa sc->sc_bus._space_map = simplebus_bs_map;
95c441d705Svisa
96c441d705Svisa sc->sc_rangeslen = OF_getproplen(sc->sc_node, "ranges");
97c441d705Svisa if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
98c441d705Svisa sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
99c441d705Svisa OF_getpropintarray(sc->sc_node, "ranges", sc->sc_ranges,
100c441d705Svisa sc->sc_rangeslen);
101c441d705Svisa }
102c441d705Svisa
103c441d705Svisa /* Scan the whole tree. */
104c441d705Svisa sc->sc_early = 1;
105c441d705Svisa for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
106c441d705Svisa simplebus_attach_node(self, node);
107c441d705Svisa
108c441d705Svisa sc->sc_early = 0;
109c441d705Svisa for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
110c441d705Svisa simplebus_attach_node(self, node);
111c441d705Svisa }
112c441d705Svisa
113c441d705Svisa int
simplebus_submatch(struct device * self,void * match,void * aux)114c441d705Svisa simplebus_submatch(struct device *self, void *match, void *aux)
115c441d705Svisa {
116c441d705Svisa struct simplebus_softc *sc = (struct simplebus_softc *)self;
117c441d705Svisa struct cfdata *cf = match;
118c441d705Svisa
119c441d705Svisa if (cf->cf_loc[0] == sc->sc_early)
120c441d705Svisa return (*cf->cf_attach->ca_match)(self, match, aux);
121c441d705Svisa return 0;
122c441d705Svisa }
123c441d705Svisa
124796a8f72Sjsg int
simplebus_print(void * aux,const char * pnp)125796a8f72Sjsg simplebus_print(void *aux, const char *pnp)
126796a8f72Sjsg {
127796a8f72Sjsg struct fdt_attach_args *fa = aux;
128796a8f72Sjsg char name[32];
129796a8f72Sjsg
130796a8f72Sjsg if (!pnp)
131796a8f72Sjsg return (QUIET);
132796a8f72Sjsg
133796a8f72Sjsg if (OF_getprop(fa->fa_node, "name", name, sizeof(name)) > 0) {
134796a8f72Sjsg name[sizeof(name) - 1] = 0;
135796a8f72Sjsg printf("\"%s\"", name);
136796a8f72Sjsg } else
137796a8f72Sjsg printf("node %u", fa->fa_node);
138796a8f72Sjsg
139796a8f72Sjsg printf(" at %s", pnp);
140796a8f72Sjsg
141796a8f72Sjsg return (UNCONF);
142796a8f72Sjsg }
143796a8f72Sjsg
144c441d705Svisa /*
145c441d705Svisa * Look for a driver that wants to be attached to this node.
146c441d705Svisa */
147c441d705Svisa void
simplebus_attach_node(struct device * self,int node)148c441d705Svisa simplebus_attach_node(struct device *self, int node)
149c441d705Svisa {
150c441d705Svisa struct simplebus_softc *sc = (struct simplebus_softc *)self;
151c441d705Svisa struct fdt_attach_args fa;
152c441d705Svisa char buffer[128];
153c441d705Svisa int i, len, line;
154c441d705Svisa uint32_t *cell, *reg;
155c441d705Svisa
156c441d705Svisa if (!OF_getprop(node, "compatible", buffer, sizeof(buffer)))
157c441d705Svisa return;
158c441d705Svisa
159c441d705Svisa if (OF_getprop(node, "status", buffer, sizeof(buffer)))
160c441d705Svisa if (!strcmp(buffer, "disabled"))
161c441d705Svisa return;
162c441d705Svisa
163c441d705Svisa memset(&fa, 0, sizeof(fa));
164c441d705Svisa fa.fa_name = "";
165c441d705Svisa fa.fa_node = node;
166c441d705Svisa fa.fa_iot = &sc->sc_bus;
167c441d705Svisa fa.fa_dmat = sc->sc_dmat;
168c441d705Svisa fa.fa_acells = sc->sc_acells;
169c441d705Svisa fa.fa_scells = sc->sc_scells;
170c441d705Svisa
171c441d705Svisa len = OF_getproplen(node, "reg");
172c441d705Svisa line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
173c441d705Svisa if (len > 0 && line > 0 && (len % line) == 0) {
174c441d705Svisa reg = malloc(len, M_TEMP, M_WAITOK);
175c441d705Svisa OF_getpropintarray(node, "reg", reg, len);
176c441d705Svisa
177c441d705Svisa fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
178c441d705Svisa M_DEVBUF, M_WAITOK | M_ZERO);
179c441d705Svisa fa.fa_nreg = (len / line);
180c441d705Svisa
181c441d705Svisa for (i = 0, cell = reg; i < len / line; i++) {
182c441d705Svisa if (sc->sc_acells >= 1)
183c441d705Svisa fa.fa_reg[i].addr = cell[0];
184c441d705Svisa if (sc->sc_acells == 2) {
185c441d705Svisa fa.fa_reg[i].addr <<= 32;
186c441d705Svisa fa.fa_reg[i].addr |= cell[1];
187c441d705Svisa }
188c441d705Svisa cell += sc->sc_acells;
189c441d705Svisa if (sc->sc_scells >= 1)
190c441d705Svisa fa.fa_reg[i].size = cell[0];
191c441d705Svisa if (sc->sc_scells == 2) {
192c441d705Svisa fa.fa_reg[i].size <<= 32;
193c441d705Svisa fa.fa_reg[i].size |= cell[1];
194c441d705Svisa }
195c441d705Svisa cell += sc->sc_scells;
196c441d705Svisa }
197c441d705Svisa
198c441d705Svisa free(reg, M_TEMP, len);
199c441d705Svisa }
200c441d705Svisa
201c441d705Svisa len = OF_getproplen(node, "interrupts");
202c441d705Svisa if (len > 0 && (len % sizeof(uint32_t)) == 0) {
203c441d705Svisa fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
204c441d705Svisa fa.fa_nintr = len / sizeof(uint32_t);
205c441d705Svisa
206c441d705Svisa OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
207c441d705Svisa }
208c441d705Svisa
209796a8f72Sjsg config_found_sm(self, &fa, sc->sc_early ? NULL : simplebus_print,
210796a8f72Sjsg simplebus_submatch);
211c441d705Svisa
212c441d705Svisa free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
213c441d705Svisa free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
214c441d705Svisa }
215c441d705Svisa
216c441d705Svisa /*
217c441d705Svisa * Translate memory address if needed.
218c441d705Svisa */
219c441d705Svisa int
simplebus_bs_map(bus_space_tag_t t,bus_addr_t bpa,bus_size_t size,int flag,bus_space_handle_t * bshp)220c441d705Svisa simplebus_bs_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size,
221c441d705Svisa int flag, bus_space_handle_t *bshp)
222c441d705Svisa {
223c441d705Svisa struct simplebus_softc *sc = t->bus_private;
224c441d705Svisa uint64_t addr, rfrom, rto, rsize;
225c441d705Svisa uint32_t *range;
226c441d705Svisa int parent, rlen, rone;
227c441d705Svisa
228c441d705Svisa addr = bpa;
229c441d705Svisa parent = OF_parent(sc->sc_node);
230c441d705Svisa if (parent == 0)
231c441d705Svisa return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
232c441d705Svisa
233c441d705Svisa if (sc->sc_rangeslen < 0)
234c441d705Svisa return EINVAL;
235c441d705Svisa if (sc->sc_rangeslen == 0)
236c441d705Svisa return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
237c441d705Svisa
238c441d705Svisa rlen = sc->sc_rangeslen / sizeof(uint32_t);
239c441d705Svisa rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
240c441d705Svisa
241c441d705Svisa /* For each range. */
242c441d705Svisa for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) {
243c441d705Svisa /* Extract from and size, so we can see if we fit. */
244c441d705Svisa rfrom = range[0];
245c441d705Svisa if (sc->sc_acells == 2)
246c441d705Svisa rfrom = (rfrom << 32) + range[1];
247c441d705Svisa rsize = range[sc->sc_acells + sc->sc_pacells];
248c441d705Svisa if (sc->sc_scells == 2)
249c441d705Svisa rsize = (rsize << 32) +
250c441d705Svisa range[sc->sc_acells + sc->sc_pacells + 1];
251c441d705Svisa
252c441d705Svisa /* Try next, if we're not in the range. */
253c441d705Svisa if (addr < rfrom || (addr + size) > (rfrom + rsize))
254c441d705Svisa continue;
255c441d705Svisa
256c441d705Svisa /* All good, extract to address and translate. */
257c441d705Svisa rto = range[sc->sc_acells];
258c441d705Svisa if (sc->sc_pacells == 2)
259c441d705Svisa rto = (rto << 32) + range[sc->sc_acells + 1];
260c441d705Svisa
261c441d705Svisa addr -= rfrom;
262c441d705Svisa addr += rto;
263c441d705Svisa
264c441d705Svisa return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
265c441d705Svisa }
266c441d705Svisa
267c441d705Svisa return ESRCH;
268c441d705Svisa }
269