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