xref: /openbsd/sys/arch/arm64/dev/mainbus.c (revision 274d7c50)
1 /* $OpenBSD: mainbus.c,v 1.15 2019/10/23 09:27:43 patrick 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 <machine/fdt.h>
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/fdt.h>
28 #include <dev/ofw/ofw_thermal.h>
29 
30 #include <arm64/arm64/arm64var.h>
31 #include <arm64/dev/mainbus.h>
32 
33 int mainbus_match(struct device *, void *, void *);
34 void mainbus_attach(struct device *, struct device *, void *);
35 
36 void mainbus_attach_node(struct device *, int, cfmatch_t);
37 int mainbus_match_status(struct device *, void *, void *);
38 void mainbus_attach_cpus(struct device *, cfmatch_t);
39 int mainbus_match_primary(struct device *, void *, void *);
40 int mainbus_match_secondary(struct device *, void *, void *);
41 void mainbus_attach_efi(struct device *);
42 void mainbus_attach_apm(struct device *);
43 void mainbus_attach_framebuffer(struct device *);
44 
45 struct mainbus_softc {
46 	struct device		 sc_dev;
47 	int			 sc_node;
48 	bus_space_tag_t		 sc_iot;
49 	bus_dma_tag_t		 sc_dmat;
50 	int			 sc_acells;
51 	int			 sc_scells;
52 	int			*sc_ranges;
53 	int			 sc_rangeslen;
54 	int			 sc_early;
55 	int			 sc_early_nodes[64];
56 };
57 
58 struct cfattach mainbus_ca = {
59 	sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL,
60 	config_activate_children
61 };
62 
63 struct cfdriver mainbus_cd = {
64 	NULL, "mainbus", DV_DULL
65 };
66 
67 struct machine_bus_dma_tag mainbus_dma_tag = {
68 	NULL,
69 	0,
70 	_dmamap_create,
71 	_dmamap_destroy,
72 	_dmamap_load,
73 	_dmamap_load_mbuf,
74 	_dmamap_load_uio,
75 	_dmamap_load_raw,
76 	_dmamap_load_buffer,
77 	_dmamap_unload,
78 	_dmamap_sync,
79 	_dmamem_alloc,
80 	_dmamem_free,
81 	_dmamem_map,
82 	_dmamem_unmap,
83 	_dmamem_mmap,
84 };
85 
86 /*
87  * Mainbus takes care of FDT and non-FDT machines, so we
88  * always attach.
89  */
90 int
91 mainbus_match(struct device *parent, void *cfdata, void *aux)
92 {
93 	return (1);
94 }
95 
96 extern char *hw_prod;
97 extern char *hw_serial;
98 void agtimer_init(void);
99 
100 void
101 mainbus_attach(struct device *parent, struct device *self, void *aux)
102 {
103 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
104 	char prop[128];
105 	int node, len;
106 
107 	arm_intr_init_fdt();
108 	agtimer_init();
109 
110 	sc->sc_node = OF_peer(0);
111 	sc->sc_iot = &arm64_bs_tag;
112 	sc->sc_dmat = &mainbus_dma_tag;
113 	sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1);
114 	sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1);
115 
116 	len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop));
117 	if (len > 0) {
118 		printf(": %s\n", prop);
119 		hw_prod = malloc(len, M_DEVBUF, M_NOWAIT);
120 		if (hw_prod)
121 			strlcpy(hw_prod, prop, len);
122 	} else
123 		printf(": unknown model\n");
124 
125 	len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop));
126 	if (len > 0) {
127 		hw_serial = malloc(len, M_DEVBUF, M_NOWAIT);
128 		if (hw_serial)
129 			strlcpy(hw_serial, prop, len);
130 	}
131 
132 	/* Attach primary CPU first. */
133 	mainbus_attach_cpus(self, mainbus_match_primary);
134 
135 	mainbus_attach_efi(self);
136 
137 	sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges");
138 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
139 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
140 		OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges,
141 		    sc->sc_rangeslen);
142 	}
143 
144 	mainbus_attach_apm(self);
145 
146 	/* Scan the whole tree. */
147 	sc->sc_early = 1;
148 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
149 		mainbus_attach_node(self, node, NULL);
150 
151 	sc->sc_early = 0;
152 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
153 		mainbus_attach_node(self, node, NULL);
154 
155 	mainbus_attach_framebuffer(self);
156 
157 	/* Attach secondary CPUs. */
158 	mainbus_attach_cpus(self, mainbus_match_secondary);
159 
160 	thermal_init();
161 }
162 
163 int
164 mainbus_print(void *aux, const char *pnp)
165 {
166 	struct fdt_attach_args *fa = aux;
167 	char buf[32];
168 
169 	if (!pnp)
170 		return (QUIET);
171 
172 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
173 	    strcmp(buf, "disabled") == 0)
174 		return (QUIET);
175 
176 	if (OF_getprop(fa->fa_node, "name", buf, sizeof(buf)) > 0) {
177 		buf[sizeof(buf) - 1] = 0;
178 		if (strcmp(buf, "aliases") == 0 ||
179 		    strcmp(buf, "chosen") == 0 ||
180 		    strcmp(buf, "cpus") == 0 ||
181 		    strcmp(buf, "memory") == 0)
182 			return (QUIET);
183 		printf("\"%s\"", buf);
184 	} else
185 		printf("node %u", fa->fa_node);
186 
187 	printf(" at %s", pnp);
188 
189 	return (UNCONF);
190 }
191 
192 /*
193  * Look for a driver that wants to be attached to this node.
194  */
195 void
196 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch)
197 {
198 	struct mainbus_softc	*sc = (struct mainbus_softc *)self;
199 	struct fdt_attach_args	 fa;
200 	int			 i, len, line;
201 	uint32_t		*cell, *reg;
202 	struct device		*child;
203 	cfprint_t		 print = NULL;
204 
205 	/* Skip if already attached early. */
206 	for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
207 		if (sc->sc_early_nodes[i] == node)
208 			return;
209 		if (sc->sc_early_nodes[i] == 0)
210 			break;
211 	}
212 
213 	memset(&fa, 0, sizeof(fa));
214 	fa.fa_name = "";
215 	fa.fa_node = node;
216 	fa.fa_iot = sc->sc_iot;
217 	fa.fa_dmat = sc->sc_dmat;
218 	fa.fa_acells = sc->sc_acells;
219 	fa.fa_scells = sc->sc_scells;
220 
221 	len = OF_getproplen(node, "reg");
222 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
223 	if (len > 0 && (len % line) == 0) {
224 		reg = malloc(len, M_TEMP, M_WAITOK);
225 		OF_getpropintarray(node, "reg", reg, len);
226 
227 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
228 		    M_DEVBUF, M_WAITOK);
229 		fa.fa_nreg = (len / line);
230 
231 		for (i = 0, cell = reg; i < len / line; i++) {
232 			if (sc->sc_acells >= 1)
233 				fa.fa_reg[i].addr = cell[0];
234 			if (sc->sc_acells == 2) {
235 				fa.fa_reg[i].addr <<= 32;
236 				fa.fa_reg[i].addr |= cell[1];
237 			}
238 			cell += sc->sc_acells;
239 			if (sc->sc_scells >= 1)
240 				fa.fa_reg[i].size = cell[0];
241 			if (sc->sc_scells == 2) {
242 				fa.fa_reg[i].size <<= 32;
243 				fa.fa_reg[i].size |= cell[1];
244 			}
245 			cell += sc->sc_scells;
246 		}
247 
248 		free(reg, M_TEMP, len);
249 	}
250 
251 	len = OF_getproplen(node, "interrupts");
252 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
253 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
254 		fa.fa_nintr = len / sizeof(uint32_t);
255 
256 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
257 	}
258 
259 	if (OF_getproplen(node, "dma-coherent") >= 0) {
260 		fa.fa_dmat = malloc(sizeof(*sc->sc_dmat),
261 		    M_DEVBUF, M_WAITOK | M_ZERO);
262 		memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat));
263 		fa.fa_dmat->_flags |= BUS_DMA_COHERENT;
264 	}
265 
266 	if (submatch == NULL && sc->sc_early == 0)
267 		print = mainbus_print;
268 	if (submatch == NULL)
269 		submatch = mainbus_match_status;
270 
271 	child = config_found_sm(self, &fa, print, submatch);
272 
273 	/* Record nodes that we attach early. */
274 	if (child && sc->sc_early) {
275 		for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
276 			if (sc->sc_early_nodes[i] != 0)
277 				continue;
278 			sc->sc_early_nodes[i] = node;
279 			break;
280 		}
281 	}
282 
283 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
284 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
285 }
286 
287 int
288 mainbus_match_status(struct device *parent, void *match, void *aux)
289 {
290 	struct mainbus_softc *sc = (struct mainbus_softc *)parent;
291 	struct fdt_attach_args *fa = aux;
292 	struct cfdata *cf = match;
293 	char buf[32];
294 
295 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
296 	    strcmp(buf, "disabled") == 0)
297 		return 0;
298 
299 	if (cf->cf_loc[0] == sc->sc_early)
300 		return (*cf->cf_attach->ca_match)(parent, match, aux);
301 
302 	return 0;
303 }
304 
305 void
306 mainbus_attach_cpus(struct device *self, cfmatch_t match)
307 {
308 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
309 	int node = OF_finddevice("/cpus");
310 	int acells, scells;
311 	char buf[32];
312 
313 	if (node == 0)
314 		return;
315 
316 	acells = sc->sc_acells;
317 	scells = sc->sc_scells;
318 	sc->sc_acells = OF_getpropint(node, "#address-cells", 2);
319 	sc->sc_scells = OF_getpropint(node, "#size-cells", 0);
320 
321 	ncpusfound = 0;
322 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
323 		if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 &&
324 		    strcmp(buf, "cpu") == 0)
325 			ncpusfound++;
326 
327 		mainbus_attach_node(self, node, match);
328 	}
329 
330 	sc->sc_acells = acells;
331 	sc->sc_scells = scells;
332 }
333 
334 int
335 mainbus_match_primary(struct device *parent, void *match, void *aux)
336 {
337 	struct fdt_attach_args *fa = aux;
338 	struct cfdata *cf = match;
339 	uint64_t mpidr = READ_SPECIALREG(mpidr_el1);
340 
341 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF))
342 		return 0;
343 
344 	return (*cf->cf_attach->ca_match)(parent, match, aux);
345 }
346 
347 int
348 mainbus_match_secondary(struct device *parent, void *match, void *aux)
349 {
350 	struct fdt_attach_args *fa = aux;
351 	struct cfdata *cf = match;
352 	uint64_t mpidr = READ_SPECIALREG(mpidr_el1);
353 
354 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF))
355 		return 0;
356 
357 	return (*cf->cf_attach->ca_match)(parent, match, aux);
358 }
359 
360 void
361 mainbus_attach_efi(struct device *self)
362 {
363 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
364 	struct fdt_attach_args fa;
365 	int node = OF_finddevice("/chosen");
366 
367 	if (node == 0 || OF_getproplen(node, "openbsd,uefi-system-table") <= 0)
368 		return;
369 
370 	memset(&fa, 0, sizeof(fa));
371 	fa.fa_name = "efi";
372 	fa.fa_iot = sc->sc_iot;
373 	fa.fa_dmat = sc->sc_dmat;
374 	config_found(self, &fa, NULL);
375 }
376 
377 void
378 mainbus_attach_apm(struct device *self)
379 {
380 	struct fdt_attach_args fa;
381 
382 	memset(&fa, 0, sizeof(fa));
383 	fa.fa_name = "apm";
384 
385 	config_found(self, &fa, NULL);
386 }
387 
388 void
389 mainbus_attach_framebuffer(struct device *self)
390 {
391 	int node = OF_finddevice("/chosen");
392 
393 	if (node == 0)
394 		return;
395 
396 	for (node = OF_child(node); node != 0; node = OF_peer(node))
397 		mainbus_attach_node(self, node, NULL);
398 }
399