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