xref: /openbsd/sys/arch/arm64/dev/mainbus.c (revision 771fbea0)
1 /* $OpenBSD: mainbus.c,v 1.21 2021/05/05 14:13:57 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 
47 struct mainbus_softc {
48 	struct device		 sc_dev;
49 	int			 sc_node;
50 	bus_space_tag_t		 sc_iot;
51 	bus_dma_tag_t		 sc_dmat;
52 	int			 sc_acells;
53 	int			 sc_scells;
54 	int			*sc_ranges;
55 	int			 sc_rangeslen;
56 	int			 sc_early;
57 	int			 sc_early_nodes[64];
58 };
59 
60 struct cfattach mainbus_ca = {
61 	sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL,
62 	config_activate_children
63 };
64 
65 struct cfdriver mainbus_cd = {
66 	NULL, "mainbus", DV_DULL
67 };
68 
69 struct machine_bus_dma_tag mainbus_dma_tag = {
70 	NULL,
71 	0,
72 	_dmamap_create,
73 	_dmamap_destroy,
74 	_dmamap_load,
75 	_dmamap_load_mbuf,
76 	_dmamap_load_uio,
77 	_dmamap_load_raw,
78 	_dmamap_load_buffer,
79 	_dmamap_unload,
80 	_dmamap_sync,
81 	_dmamem_alloc,
82 	_dmamem_free,
83 	_dmamem_map,
84 	_dmamem_unmap,
85 	_dmamem_mmap,
86 };
87 
88 /*
89  * Mainbus takes care of FDT and non-FDT machines, so we
90  * always attach.
91  */
92 int
93 mainbus_match(struct device *parent, void *cfdata, void *aux)
94 {
95 	return (1);
96 }
97 
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 	mainbus_attach_psci(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_efi(self);
141 
142 	sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges");
143 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
144 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
145 		OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges,
146 		    sc->sc_rangeslen);
147 	}
148 
149 	mainbus_attach_apm(self);
150 
151 	/* Scan the whole tree. */
152 	sc->sc_early = 1;
153 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
154 		mainbus_attach_node(self, node, NULL);
155 
156 	sc->sc_early = 0;
157 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
158 		mainbus_attach_node(self, node, NULL);
159 
160 	mainbus_attach_framebuffer(self);
161 
162 	thermal_init();
163 }
164 
165 int
166 mainbus_print(void *aux, const char *pnp)
167 {
168 	struct fdt_attach_args *fa = aux;
169 	char buf[32];
170 
171 	if (!pnp)
172 		return (QUIET);
173 
174 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
175 	    strcmp(buf, "disabled") == 0)
176 		return (QUIET);
177 
178 	if (OF_getprop(fa->fa_node, "name", buf, sizeof(buf)) > 0) {
179 		buf[sizeof(buf) - 1] = 0;
180 		if (strcmp(buf, "aliases") == 0 ||
181 		    strcmp(buf, "chosen") == 0 ||
182 		    strcmp(buf, "cpus") == 0 ||
183 		    strcmp(buf, "memory") == 0 ||
184 		    strcmp(buf, "reserved-memory") == 0 ||
185 		    strcmp(buf, "thermal-zones") == 0 ||
186 		    strncmp(buf, "__", 2) == 0)
187 			return (QUIET);
188 		printf("\"%s\"", buf);
189 	} else
190 		printf("node %u", fa->fa_node);
191 
192 	printf(" at %s", pnp);
193 
194 	return (UNCONF);
195 }
196 
197 /*
198  * Look for a driver that wants to be attached to this node.
199  */
200 void
201 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch)
202 {
203 	struct mainbus_softc	*sc = (struct mainbus_softc *)self;
204 	struct fdt_attach_args	 fa;
205 	int			 i, len, line;
206 	uint32_t		*cell, *reg;
207 	struct device		*child;
208 	cfprint_t		 print = NULL;
209 
210 	/* Skip if already attached early. */
211 	for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
212 		if (sc->sc_early_nodes[i] == node)
213 			return;
214 		if (sc->sc_early_nodes[i] == 0)
215 			break;
216 	}
217 
218 	memset(&fa, 0, sizeof(fa));
219 	fa.fa_name = "";
220 	fa.fa_node = node;
221 	fa.fa_iot = sc->sc_iot;
222 	fa.fa_dmat = sc->sc_dmat;
223 	fa.fa_acells = sc->sc_acells;
224 	fa.fa_scells = sc->sc_scells;
225 
226 	len = OF_getproplen(node, "reg");
227 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
228 	if (len > 0 && (len % line) == 0) {
229 		reg = malloc(len, M_TEMP, M_WAITOK);
230 		OF_getpropintarray(node, "reg", reg, len);
231 
232 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
233 		    M_DEVBUF, M_WAITOK);
234 		fa.fa_nreg = (len / line);
235 
236 		for (i = 0, cell = reg; i < len / line; i++) {
237 			if (sc->sc_acells >= 1)
238 				fa.fa_reg[i].addr = cell[0];
239 			if (sc->sc_acells == 2) {
240 				fa.fa_reg[i].addr <<= 32;
241 				fa.fa_reg[i].addr |= cell[1];
242 			}
243 			cell += sc->sc_acells;
244 			if (sc->sc_scells >= 1)
245 				fa.fa_reg[i].size = cell[0];
246 			if (sc->sc_scells == 2) {
247 				fa.fa_reg[i].size <<= 32;
248 				fa.fa_reg[i].size |= cell[1];
249 			}
250 			cell += sc->sc_scells;
251 		}
252 
253 		free(reg, M_TEMP, len);
254 	}
255 
256 	len = OF_getproplen(node, "interrupts");
257 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
258 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
259 		fa.fa_nintr = len / sizeof(uint32_t);
260 
261 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
262 	}
263 
264 	if (OF_getproplen(node, "dma-coherent") >= 0) {
265 		fa.fa_dmat = malloc(sizeof(*sc->sc_dmat),
266 		    M_DEVBUF, M_WAITOK | M_ZERO);
267 		memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat));
268 		fa.fa_dmat->_flags |= BUS_DMA_COHERENT;
269 	}
270 
271 	fa.fa_dmat = iommu_device_map(fa.fa_node, fa.fa_dmat);
272 
273 	if (submatch == NULL && sc->sc_early == 0)
274 		print = mainbus_print;
275 	if (submatch == NULL)
276 		submatch = mainbus_match_status;
277 
278 	child = config_found_sm(self, &fa, print, submatch);
279 
280 	/* Record nodes that we attach early. */
281 	if (child && sc->sc_early) {
282 		for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
283 			if (sc->sc_early_nodes[i] != 0)
284 				continue;
285 			sc->sc_early_nodes[i] = node;
286 			break;
287 		}
288 	}
289 
290 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
291 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
292 }
293 
294 int
295 mainbus_match_status(struct device *parent, void *match, void *aux)
296 {
297 	struct mainbus_softc *sc = (struct mainbus_softc *)parent;
298 	struct fdt_attach_args *fa = aux;
299 	struct cfdata *cf = match;
300 	char buf[32];
301 
302 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
303 	    strcmp(buf, "disabled") == 0)
304 		return 0;
305 
306 	if (cf->cf_loc[0] == sc->sc_early)
307 		return (*cf->cf_attach->ca_match)(parent, match, aux);
308 
309 	return 0;
310 }
311 
312 void
313 mainbus_attach_cpus(struct device *self, cfmatch_t match)
314 {
315 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
316 	int node = OF_finddevice("/cpus");
317 	int acells, scells;
318 	char buf[32];
319 
320 	if (node == -1)
321 		return;
322 
323 	acells = sc->sc_acells;
324 	scells = sc->sc_scells;
325 	sc->sc_acells = OF_getpropint(node, "#address-cells", 2);
326 	sc->sc_scells = OF_getpropint(node, "#size-cells", 0);
327 
328 	ncpusfound = 0;
329 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
330 		if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 &&
331 		    strcmp(buf, "cpu") == 0)
332 			ncpusfound++;
333 
334 		mainbus_attach_node(self, node, match);
335 	}
336 
337 	sc->sc_acells = acells;
338 	sc->sc_scells = scells;
339 }
340 
341 int
342 mainbus_match_primary(struct device *parent, void *match, void *aux)
343 {
344 	struct fdt_attach_args *fa = aux;
345 	struct cfdata *cf = match;
346 	uint64_t mpidr = READ_SPECIALREG(mpidr_el1);
347 
348 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF))
349 		return 0;
350 
351 	return (*cf->cf_attach->ca_match)(parent, match, aux);
352 }
353 
354 int
355 mainbus_match_secondary(struct device *parent, void *match, void *aux)
356 {
357 	struct fdt_attach_args *fa = aux;
358 	struct cfdata *cf = match;
359 	uint64_t mpidr = READ_SPECIALREG(mpidr_el1);
360 
361 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF))
362 		return 0;
363 
364 	return (*cf->cf_attach->ca_match)(parent, match, aux);
365 }
366 
367 void
368 mainbus_attach_psci(struct device *self)
369 {
370 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
371 	int node = OF_finddevice("/psci");
372 
373 	if (node == -1)
374 		return;
375 
376 	sc->sc_early = 1;
377 	mainbus_attach_node(self, node, NULL);
378 	sc->sc_early = 0;
379 }
380 
381 void
382 mainbus_attach_efi(struct device *self)
383 {
384 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
385 	struct fdt_attach_args fa;
386 	int node = OF_finddevice("/chosen");
387 
388 	if (node == -1 ||
389 	    OF_getproplen(node, "openbsd,uefi-system-table") <= 0)
390 		return;
391 
392 	memset(&fa, 0, sizeof(fa));
393 	fa.fa_name = "efi";
394 	fa.fa_iot = sc->sc_iot;
395 	fa.fa_dmat = sc->sc_dmat;
396 	config_found(self, &fa, NULL);
397 }
398 
399 void
400 mainbus_attach_apm(struct device *self)
401 {
402 	struct fdt_attach_args fa;
403 
404 	memset(&fa, 0, sizeof(fa));
405 	fa.fa_name = "apm";
406 
407 	config_found(self, &fa, NULL);
408 }
409 
410 void
411 mainbus_attach_framebuffer(struct device *self)
412 {
413 	int node = OF_finddevice("/chosen");
414 
415 	if (node == -1)
416 		return;
417 
418 	for (node = OF_child(node); node != 0; node = OF_peer(node))
419 		mainbus_attach_node(self, node, NULL);
420 }
421