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