xref: /openbsd-src/sys/arch/arm/simplebus/simplebus.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: simplebus.c,v 1.9 2016/09/18 17:50:26 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 <arm/fdt.h>
28 
29 int simplebus_match(struct device *, void *, void *);
30 void simplebus_attach(struct device *, struct device *, void *);
31 
32 void simplebus_attach_node(struct device *, int);
33 int simplebus_bs_map(void *, bus_addr_t, bus_size_t, int, bus_space_handle_t *);
34 
35 struct simplebus_softc {
36 	struct device		 sc_dev;
37 	int			 sc_node;
38 	bus_space_tag_t		 sc_iot;
39 	bus_dma_tag_t		 sc_dmat;
40 	int			 sc_acells;
41 	int			 sc_scells;
42 	int			 sc_pacells;
43 	int			 sc_pscells;
44 	struct bus_space	 sc_bus;
45 	int			*sc_ranges;
46 	int			 sc_rangeslen;
47 	int			 sc_early;
48 };
49 
50 struct cfattach simplebus_ca = {
51 	sizeof(struct simplebus_softc), simplebus_match, simplebus_attach, NULL,
52 	config_activate_children
53 };
54 
55 struct cfdriver simplebus_cd = {
56 	NULL, "simplebus", DV_DULL
57 };
58 
59 /*
60  * Simplebus is a generic bus with no special casings.
61  */
62 int
63 simplebus_match(struct device *parent, void *cfdata, void *aux)
64 {
65 	struct fdt_attach_args *fa = (struct fdt_attach_args *)aux;
66 
67 	if (fa->fa_node == 0)
68 		return (0);
69 
70 	if (!OF_is_compatible(fa->fa_node, "simple-bus"))
71 		return (0);
72 
73 	return (1);
74 }
75 
76 void
77 simplebus_attach(struct device *parent, struct device *self, void *aux)
78 {
79 	struct simplebus_softc *sc = (struct simplebus_softc *)self;
80 	struct fdt_attach_args *fa = (struct fdt_attach_args *)aux;
81 	char name[32];
82 	int node;
83 
84 	sc->sc_node = fa->fa_node;
85 	sc->sc_iot = fa->fa_iot;
86 	sc->sc_dmat = fa->fa_dmat;
87 	sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells",
88 	    fa->fa_acells);
89 	sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells",
90 	    fa->fa_scells);
91 	sc->sc_pacells = fa->fa_acells;
92 	sc->sc_pscells = fa->fa_scells;
93 
94 	if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) {
95 		name[sizeof(name) - 1] = 0;
96 		printf(": \"%s\"", name);
97 	}
98 
99 	printf("\n");
100 
101 	memcpy(&sc->sc_bus, sc->sc_iot, sizeof(sc->sc_bus));
102 	sc->sc_bus.bs_cookie = sc;
103 	sc->sc_bus.bs_map = simplebus_bs_map;
104 
105 	sc->sc_rangeslen = OF_getproplen(sc->sc_node, "ranges");
106 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
107 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
108 		OF_getpropintarray(sc->sc_node, "ranges", sc->sc_ranges,
109 		    sc->sc_rangeslen);
110 	}
111 
112 	/* Scan the whole tree. */
113 	sc->sc_early = 1;
114 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
115 		simplebus_attach_node(self, node);
116 
117 	sc->sc_early = 0;
118 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node))
119 		simplebus_attach_node(self, node);
120 }
121 
122 int
123 simplebus_submatch(struct device *self, void *match, void *aux)
124 {
125 	struct simplebus_softc	*sc = (struct simplebus_softc *)self;
126 	struct cfdata *cf = match;
127 
128 	if (cf->cf_loc[0] == sc->sc_early)
129 		return (*cf->cf_attach->ca_match)(self, match, aux);
130 	return 0;
131 }
132 
133 /*
134  * Look for a driver that wants to be attached to this node.
135  */
136 void
137 simplebus_attach_node(struct device *self, int node)
138 {
139 	struct simplebus_softc	*sc = (struct simplebus_softc *)self;
140 	struct fdt_attach_args	 fa;
141 	char			 buffer[128];
142 	int			 i, len, line;
143 	uint32_t		*cell, *reg;
144 
145 	if (!OF_getprop(node, "compatible", buffer, sizeof(buffer)))
146 		return;
147 
148 	if (OF_getprop(node, "status", buffer, sizeof(buffer)))
149 		if (!strcmp(buffer, "disabled"))
150 			return;
151 
152 	memset(&fa, 0, sizeof(fa));
153 	fa.fa_name = "";
154 	fa.fa_node = node;
155 	fa.fa_iot = &sc->sc_bus;
156 	fa.fa_dmat = sc->sc_dmat;
157 	fa.fa_acells = sc->sc_acells;
158 	fa.fa_scells = sc->sc_scells;
159 
160 	len = OF_getproplen(node, "reg");
161 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
162 	if (len > 0 && line > 0 && (len % line) == 0) {
163 		reg = malloc(len, M_TEMP, M_WAITOK);
164 		OF_getpropintarray(node, "reg", reg, len);
165 
166 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
167 		    M_DEVBUF, M_WAITOK | M_ZERO);
168 		fa.fa_nreg = (len / line);
169 
170 		for (i = 0, cell = reg; i < len / line; i++) {
171 			if (sc->sc_acells >= 1)
172 				fa.fa_reg[i].addr = cell[0];
173 			if (sc->sc_acells == 2) {
174 				fa.fa_reg[i].addr <<= 32;
175 				fa.fa_reg[i].addr |= cell[1];
176 			}
177 			cell += sc->sc_acells;
178 			if (sc->sc_scells >= 1)
179 				fa.fa_reg[i].size = cell[0];
180 			if (sc->sc_scells == 2) {
181 				fa.fa_reg[i].size <<= 32;
182 				fa.fa_reg[i].size |= cell[1];
183 			}
184 			cell += sc->sc_scells;
185 		}
186 
187 		free(reg, M_TEMP, len);
188 	}
189 
190 	len = OF_getproplen(node, "interrupts");
191 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
192 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
193 		fa.fa_nintr = len / sizeof(uint32_t);
194 
195 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
196 	}
197 
198 	config_found_sm(self, &fa, NULL, simplebus_submatch);
199 
200 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
201 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
202 }
203 
204 /*
205  * Translate memory address if needed.
206  */
207 int
208 simplebus_bs_map(void *t, bus_addr_t bpa, bus_size_t size,
209     int flag, bus_space_handle_t *bshp)
210 {
211 	struct simplebus_softc *sc = (struct simplebus_softc *)t;
212 	uint64_t addr, rfrom, rto, rsize;
213 	uint32_t *range;
214 	int parent, rlen, rone;
215 
216 	addr = bpa;
217 	parent = OF_parent(sc->sc_node);
218 	if (parent == 0)
219 		return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
220 
221 	if (sc->sc_rangeslen < 0)
222 		return EINVAL;
223 	if (sc->sc_rangeslen == 0)
224 		return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
225 
226 	rlen = sc->sc_rangeslen / sizeof(uint32_t);
227 	rone = sc->sc_pacells + sc->sc_acells + sc->sc_scells;
228 
229 	/* For each range. */
230 	for (range = sc->sc_ranges; rlen >= rone; rlen -= rone, range += rone) {
231 		/* Extract from and size, so we can see if we fit. */
232 		rfrom = range[0];
233 		if (sc->sc_acells == 2)
234 			rfrom = (rfrom << 32) + range[1];
235 		rsize = range[sc->sc_acells + sc->sc_pacells];
236 		if (sc->sc_scells == 2)
237 			rsize = (rsize << 32) +
238 			    range[sc->sc_acells + sc->sc_pacells + 1];
239 
240 		/* Try next, if we're not in the range. */
241 		if (addr < rfrom || (addr + size) > (rfrom + rsize))
242 			continue;
243 
244 		/* All good, extract to address and translate. */
245 		rto = range[sc->sc_acells];
246 		if (sc->sc_pacells == 2)
247 			rto = (rto << 32) + range[sc->sc_acells + 1];
248 
249 		addr -= rfrom;
250 		addr += rto;
251 
252 		return bus_space_map(sc->sc_iot, addr, size, flag, bshp);
253 	}
254 
255 	return ESRCH;
256 }
257