xref: /openbsd/sys/arch/arm/mainbus/mainbus.c (revision 20584b0d)
1 /* $OpenBSD: mainbus.c,v 1.20 2019/09/21 15:57:03 kettenis Exp $ */
2 /*
3  * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se>
4  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24 
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/fdt.h>
27 #include <dev/ofw/ofw_thermal.h>
28 
29 #include <arm/mainbus/mainbus.h>
30 
31 int mainbus_match(struct device *, void *, void *);
32 void mainbus_attach(struct device *, struct device *, void *);
33 
34 void mainbus_attach_node(struct device *, int, cfmatch_t);
35 int mainbus_match_status(struct device *, void *, void *);
36 void mainbus_attach_cpus(struct device *, cfmatch_t);
37 int mainbus_match_primary(struct device *, void *, void *);
38 int mainbus_match_secondary(struct device *, void *, void *);
39 void mainbus_attach_framebuffer(struct device *);
40 
41 struct mainbus_softc {
42 	struct device		 sc_dev;
43 	int			 sc_node;
44 	bus_space_tag_t		 sc_iot;
45 	bus_dma_tag_t		 sc_dmat;
46 	int			 sc_acells;
47 	int			 sc_scells;
48 	int			*sc_ranges;
49 	int			 sc_rangeslen;
50 	int			 sc_early;
51 };
52 
53 struct cfattach mainbus_ca = {
54 	sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL,
55 	config_activate_children
56 };
57 
58 struct cfdriver mainbus_cd = {
59 	NULL, "mainbus", DV_DULL
60 };
61 
62 struct arm32_bus_dma_tag mainbus_dma_tag = {
63 	0,
64 	0,
65 	NULL,
66 	_bus_dmamap_create,
67 	_bus_dmamap_destroy,
68 	_bus_dmamap_load,
69 	_bus_dmamap_load_mbuf,
70 	_bus_dmamap_load_uio,
71 	_bus_dmamap_load_raw,
72 	_bus_dmamap_unload,
73 	_bus_dmamap_sync,
74 	_bus_dmamem_alloc,
75 	_bus_dmamem_free,
76 	_bus_dmamem_map,
77 	_bus_dmamem_unmap,
78 	_bus_dmamem_mmap,
79 };
80 
81 /*
82  * Mainbus takes care of FDT and non-FDT machines, so we
83  * always attach.
84  */
85 int
86 mainbus_match(struct device *parent, void *cfdata, void *aux)
87 {
88 	return (1);
89 }
90 
91 extern char *hw_prod;
92 extern struct bus_space armv7_bs_tag;
93 void platform_init_mainbus(struct device *);
94 
95 void
96 mainbus_attach(struct device *parent, struct device *self, void *aux)
97 {
98 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
99 	char model[128];
100 	int node, len;
101 
102 	arm_intr_init_fdt();
103 
104 	sc->sc_node = OF_peer(0);
105 	sc->sc_iot = &armv7_bs_tag;
106 	sc->sc_dmat = &mainbus_dma_tag;
107 	sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1);
108 	sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1);
109 
110 	len = OF_getprop(sc->sc_node, "model", model, sizeof(model));
111 	if (len > 0) {
112 		printf(": %s\n", model);
113 		hw_prod = malloc(len, M_DEVBUF, M_NOWAIT);
114 		if (hw_prod)
115 			strlcpy(hw_prod, model, len);
116 	} else
117 		printf(": unknown model\n");
118 
119 	/* Attach primary CPU first. */
120 	mainbus_attach_cpus(self, mainbus_match_primary);
121 
122 	platform_init_mainbus(self);
123 
124 	sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges");
125 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
126 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
127 		OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges,
128 		    sc->sc_rangeslen);
129 	}
130 
131 	/* Scan the whole tree. */
132 	sc->sc_early = 1;
133 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
134 		mainbus_attach_node(self, node, NULL);
135 
136 	sc->sc_early = 0;
137 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
138 		mainbus_attach_node(self, node, NULL);
139 
140 	mainbus_attach_framebuffer(self);
141 
142 	/* Attach secondary CPUs. */
143 	mainbus_attach_cpus(self, mainbus_match_secondary);
144 
145 	thermal_init();
146 }
147 
148 /*
149  * Look for a driver that wants to be attached to this node.
150  */
151 void
152 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch)
153 {
154 	struct mainbus_softc	*sc = (struct mainbus_softc *)self;
155 	struct fdt_attach_args	 fa;
156 	int			 i, len, line;
157 	uint32_t		*cell, *reg;
158 
159 	memset(&fa, 0, sizeof(fa));
160 	fa.fa_name = "";
161 	fa.fa_node = node;
162 	fa.fa_iot = sc->sc_iot;
163 	fa.fa_dmat = sc->sc_dmat;
164 	fa.fa_acells = sc->sc_acells;
165 	fa.fa_scells = sc->sc_scells;
166 
167 	len = OF_getproplen(node, "reg");
168 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
169 	if (len > 0 && (len % line) == 0) {
170 		reg = malloc(len, M_TEMP, M_WAITOK);
171 		OF_getpropintarray(node, "reg", reg, len);
172 
173 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
174 		    M_DEVBUF, M_WAITOK);
175 		fa.fa_nreg = (len / line);
176 
177 		for (i = 0, cell = reg; i < len / line; i++) {
178 			if (sc->sc_acells >= 1)
179 				fa.fa_reg[i].addr = cell[0];
180 			if (sc->sc_acells == 2) {
181 				fa.fa_reg[i].addr <<= 32;
182 				fa.fa_reg[i].addr |= cell[1];
183 			}
184 			cell += sc->sc_acells;
185 			if (sc->sc_scells >= 1)
186 				fa.fa_reg[i].size = cell[0];
187 			if (sc->sc_scells == 2) {
188 				fa.fa_reg[i].size <<= 32;
189 				fa.fa_reg[i].size |= cell[1];
190 			}
191 			cell += sc->sc_scells;
192 		}
193 
194 		free(reg, M_TEMP, len);
195 	}
196 
197 	len = OF_getproplen(node, "interrupts");
198 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
199 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
200 		fa.fa_nintr = len / sizeof(uint32_t);
201 
202 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
203 	}
204 
205 	if (submatch == NULL)
206 		submatch = mainbus_match_status;
207 	config_found_sm(self, &fa, NULL, submatch);
208 
209 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
210 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
211 }
212 
213 int
214 mainbus_match_status(struct device *parent, void *match, void *aux)
215 {
216 	struct mainbus_softc *sc = (struct mainbus_softc *)parent;
217 	struct fdt_attach_args *fa = aux;
218 	struct cfdata *cf = match;
219 	char buf[32];
220 
221 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
222 	    strcmp(buf, "disabled") == 0)
223 		return 0;
224 
225 	if (cf->cf_loc[0] == sc->sc_early)
226 		return (*cf->cf_attach->ca_match)(parent, match, aux);
227 	return 0;
228 }
229 
230 void
231 mainbus_attach_cpus(struct device *self, cfmatch_t match)
232 {
233 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
234 	int node = OF_finddevice("/cpus");
235 	int acells, scells;
236 
237 	if (node == 0)
238 		return;
239 
240 	acells = sc->sc_acells;
241 	scells = sc->sc_scells;
242 	sc->sc_acells = OF_getpropint(node, "#address-cells", 1);
243 	sc->sc_scells = OF_getpropint(node, "#size-cells", 0);
244 
245 	for (node = OF_child(node); node != 0; node = OF_peer(node))
246 		mainbus_attach_node(self, node, match);
247 
248 	sc->sc_acells = acells;
249 	sc->sc_scells = scells;
250 }
251 
252 int
253 mainbus_match_primary(struct device *parent, void *match, void *aux)
254 {
255 	struct fdt_attach_args *fa = aux;
256 	struct cfdata *cf = match;
257 	uint32_t mpidr;
258 
259 	__asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr));
260 
261 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF))
262 		return 0;
263 
264 	return (*cf->cf_attach->ca_match)(parent, match, aux);
265 }
266 
267 int
268 mainbus_match_secondary(struct device *parent, void *match, void *aux)
269 {
270 	struct fdt_attach_args *fa = aux;
271 	struct cfdata *cf = match;
272 	uint32_t mpidr;
273 
274 	__asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr));
275 
276 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF))
277 		return 0;
278 
279 	return (*cf->cf_attach->ca_match)(parent, match, aux);
280 }
281 
282 void
283 mainbus_attach_framebuffer(struct device *self)
284 {
285 	int node = OF_finddevice("/chosen");
286 
287 	if (node == 0)
288 		return;
289 
290 	for (node = OF_child(node); node != 0; node = OF_peer(node))
291 		mainbus_attach_node(self, node, NULL);
292 }
293 
294 /*
295  * Legacy support for SoCs that do not fully use FDT.
296  */
297 void
298 mainbus_legacy_found(struct device *self, char *name)
299 {
300 	union mainbus_attach_args ma;
301 
302 	memset(&ma, 0, sizeof(ma));
303 	ma.ma_name = name;
304 
305 	config_found(self, &ma, NULL);
306 }
307