xref: /openbsd-src/sys/arch/riscv64/dev/mainbus.c (revision b0d2811898b6af5f105845a12bf9d62865077da0)
1*b0d28118Sjsg /*	$OpenBSD: mainbus.c,v 1.13 2024/11/18 05:32:39 jsg Exp $ */
2380aa7b9Sjsg 
3baed8f06Sdrahn /*
4baed8f06Sdrahn  * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se>
5baed8f06Sdrahn  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
6baed8f06Sdrahn  *
7baed8f06Sdrahn  * Permission to use, copy, modify, and distribute this software for any
8baed8f06Sdrahn  * purpose with or without fee is hereby granted, provided that the above
9baed8f06Sdrahn  * copyright notice and this permission notice appear in all copies.
10baed8f06Sdrahn  *
11baed8f06Sdrahn  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12baed8f06Sdrahn  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13baed8f06Sdrahn  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14baed8f06Sdrahn  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15baed8f06Sdrahn  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16baed8f06Sdrahn  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17baed8f06Sdrahn  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18baed8f06Sdrahn  */
19baed8f06Sdrahn 
20baed8f06Sdrahn #include <sys/param.h>
21baed8f06Sdrahn #include <sys/systm.h>
22baed8f06Sdrahn #include <sys/device.h>
23baed8f06Sdrahn #include <sys/malloc.h>
24baed8f06Sdrahn 
25baed8f06Sdrahn #include <machine/fdt.h>
26baed8f06Sdrahn #include <dev/ofw/openfirm.h>
27baed8f06Sdrahn #include <dev/ofw/fdt.h>
28baed8f06Sdrahn 
29baed8f06Sdrahn int mainbus_match(struct device *, void *, void *);
30baed8f06Sdrahn void mainbus_attach(struct device *, struct device *, void *);
31baed8f06Sdrahn 
32baed8f06Sdrahn void mainbus_attach_node(struct device *, int, cfmatch_t);
33baed8f06Sdrahn int mainbus_match_status(struct device *, void *, void *);
34baed8f06Sdrahn void mainbus_attach_cpus(struct device *, cfmatch_t);
35baed8f06Sdrahn int mainbus_match_primary(struct device *, void *, void *);
36baed8f06Sdrahn int mainbus_match_secondary(struct device *, void *, void *);
37baed8f06Sdrahn void mainbus_attach_framebuffer(struct device *);
38baed8f06Sdrahn 
39baed8f06Sdrahn struct mainbus_softc {
40baed8f06Sdrahn 	struct device		 sc_dev;
41baed8f06Sdrahn 	int			 sc_node;
42baed8f06Sdrahn 	bus_space_tag_t		 sc_iot;
43baed8f06Sdrahn 	bus_dma_tag_t		 sc_dmat;
44baed8f06Sdrahn 	int			 sc_acells;
45baed8f06Sdrahn 	int			 sc_scells;
46baed8f06Sdrahn 	int			*sc_ranges;
47baed8f06Sdrahn 	int			 sc_rangeslen;
48baed8f06Sdrahn 	int			 sc_early;
49f33bf8ceSkettenis 	int			 sc_early_nodes[64];
50baed8f06Sdrahn };
51baed8f06Sdrahn 
52baed8f06Sdrahn const struct cfattach mainbus_ca = {
53130ea1ecSderaadt 	sizeof(struct mainbus_softc), mainbus_match, mainbus_attach
54baed8f06Sdrahn };
55baed8f06Sdrahn 
56baed8f06Sdrahn struct cfdriver mainbus_cd = {
57baed8f06Sdrahn 	NULL, "mainbus", DV_DULL
58baed8f06Sdrahn };
59baed8f06Sdrahn 
60baed8f06Sdrahn struct machine_bus_dma_tag mainbus_dma_tag = {
61baed8f06Sdrahn 	NULL,
6216c51e1aSkettenis 	BUS_DMA_COHERENT,
63baed8f06Sdrahn 	_dmamap_create,
64baed8f06Sdrahn 	_dmamap_destroy,
65baed8f06Sdrahn 	_dmamap_load,
66baed8f06Sdrahn 	_dmamap_load_mbuf,
67baed8f06Sdrahn 	_dmamap_load_uio,
68baed8f06Sdrahn 	_dmamap_load_raw,
69baed8f06Sdrahn 	_dmamap_load_buffer,
70baed8f06Sdrahn 	_dmamap_unload,
71baed8f06Sdrahn 	_dmamap_sync,
72baed8f06Sdrahn 	_dmamem_alloc,
73baed8f06Sdrahn 	_dmamem_free,
74baed8f06Sdrahn 	_dmamem_map,
75baed8f06Sdrahn 	_dmamem_unmap,
76baed8f06Sdrahn 	_dmamem_mmap,
77baed8f06Sdrahn };
78baed8f06Sdrahn 
79baed8f06Sdrahn /*
80baed8f06Sdrahn  * Mainbus takes care of FDT and non-FDT machines, so we
81baed8f06Sdrahn  * always attach.
82baed8f06Sdrahn  */
83baed8f06Sdrahn int
84baed8f06Sdrahn mainbus_match(struct device *parent, void *cfdata, void *aux)
85baed8f06Sdrahn {
86baed8f06Sdrahn 	return (1);
87baed8f06Sdrahn }
88baed8f06Sdrahn 
89baed8f06Sdrahn void
90baed8f06Sdrahn mainbus_attach(struct device *parent, struct device *self, void *aux)
91baed8f06Sdrahn {
92baed8f06Sdrahn 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
93f33bf8ceSkettenis 	char prop[128];
94baed8f06Sdrahn 	int node, len;
95baed8f06Sdrahn 
96baed8f06Sdrahn 	riscv_intr_init_fdt();
97baed8f06Sdrahn 
98baed8f06Sdrahn 	sc->sc_node = OF_peer(0);
99baed8f06Sdrahn 	sc->sc_iot = &riscv64_bs_tag;
100baed8f06Sdrahn 	sc->sc_dmat = &mainbus_dma_tag;
101baed8f06Sdrahn 	sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1);
102baed8f06Sdrahn 	sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1);
103baed8f06Sdrahn 
104f33bf8ceSkettenis 	len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop));
105baed8f06Sdrahn 	if (len > 0) {
106f33bf8ceSkettenis 		printf(": %s\n", prop);
107baed8f06Sdrahn 		hw_prod = malloc(len, M_DEVBUF, M_NOWAIT);
108baed8f06Sdrahn 		if (hw_prod)
109f33bf8ceSkettenis 			strlcpy(hw_prod, prop, len);
110baed8f06Sdrahn 	} else
111baed8f06Sdrahn 		printf(": unknown model\n");
112baed8f06Sdrahn 
113f33bf8ceSkettenis 	len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop));
114f33bf8ceSkettenis 	if (len > 0) {
115f33bf8ceSkettenis 		hw_serial = malloc(len, M_DEVBUF, M_NOWAIT);
116f33bf8ceSkettenis 		if (hw_serial)
117f33bf8ceSkettenis 			strlcpy(hw_serial, prop, len);
118f33bf8ceSkettenis 	}
119f33bf8ceSkettenis 
120baed8f06Sdrahn 	/* Attach primary CPU first. */
121baed8f06Sdrahn 	mainbus_attach_cpus(self, mainbus_match_primary);
122baed8f06Sdrahn 
123f33bf8ceSkettenis 	/* Attach secondary CPUs. */
124f33bf8ceSkettenis 	mainbus_attach_cpus(self, mainbus_match_secondary);
125f33bf8ceSkettenis 
126baed8f06Sdrahn 	sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges");
127baed8f06Sdrahn 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
128baed8f06Sdrahn 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
129baed8f06Sdrahn 		OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges,
130baed8f06Sdrahn 		    sc->sc_rangeslen);
131baed8f06Sdrahn 	}
132baed8f06Sdrahn 
133baed8f06Sdrahn 	/* Scan the whole tree. */
134baed8f06Sdrahn 	sc->sc_early = 1;
135baed8f06Sdrahn 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
136baed8f06Sdrahn 		mainbus_attach_node(self, node, NULL);
137baed8f06Sdrahn 
138baed8f06Sdrahn 	sc->sc_early = 0;
139baed8f06Sdrahn 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
140baed8f06Sdrahn 		mainbus_attach_node(self, node, NULL);
141baed8f06Sdrahn 
142baed8f06Sdrahn 	mainbus_attach_framebuffer(self);
143f33bf8ceSkettenis }
144baed8f06Sdrahn 
145f33bf8ceSkettenis int
146f33bf8ceSkettenis mainbus_print(void *aux, const char *pnp)
147f33bf8ceSkettenis {
148f33bf8ceSkettenis 	struct fdt_attach_args *fa = aux;
149f33bf8ceSkettenis 	char buf[32];
150f33bf8ceSkettenis 
151f33bf8ceSkettenis 	if (!pnp)
152f33bf8ceSkettenis 		return (QUIET);
153f33bf8ceSkettenis 
154f33bf8ceSkettenis 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
155f33bf8ceSkettenis 	    strcmp(buf, "disabled") == 0)
156f33bf8ceSkettenis 		return (QUIET);
157f33bf8ceSkettenis 
158f33bf8ceSkettenis 	if (OF_getprop(fa->fa_node, "name", buf, sizeof(buf)) > 0) {
159f33bf8ceSkettenis 		buf[sizeof(buf) - 1] = 0;
160f33bf8ceSkettenis 		if (strcmp(buf, "aliases") == 0 ||
161f33bf8ceSkettenis 		    strcmp(buf, "chosen") == 0 ||
162f33bf8ceSkettenis 		    strcmp(buf, "cpus") == 0 ||
163f33bf8ceSkettenis 		    strcmp(buf, "memory") == 0 ||
164f33bf8ceSkettenis 		    strcmp(buf, "reserved-memory") == 0 ||
165f33bf8ceSkettenis 		    strcmp(buf, "thermal-zones") == 0 ||
166f33bf8ceSkettenis 		    strncmp(buf, "__", 2) == 0)
167f33bf8ceSkettenis 			return (QUIET);
168f33bf8ceSkettenis 		printf("\"%s\"", buf);
169f33bf8ceSkettenis 	} else
170f33bf8ceSkettenis 		printf("node %u", fa->fa_node);
171f33bf8ceSkettenis 
172f33bf8ceSkettenis 	printf(" at %s", pnp);
173f33bf8ceSkettenis 
174f33bf8ceSkettenis 	return (UNCONF);
175baed8f06Sdrahn }
176baed8f06Sdrahn 
177baed8f06Sdrahn /*
178baed8f06Sdrahn  * Look for a driver that wants to be attached to this node.
179baed8f06Sdrahn  */
180baed8f06Sdrahn void
181baed8f06Sdrahn mainbus_attach_node(struct device *self, int node, cfmatch_t submatch)
182baed8f06Sdrahn {
183baed8f06Sdrahn 	struct mainbus_softc	*sc = (struct mainbus_softc *)self;
184baed8f06Sdrahn 	struct fdt_attach_args	 fa;
185baed8f06Sdrahn 	int			 i, len, line;
186baed8f06Sdrahn 	uint32_t		*cell, *reg;
187f33bf8ceSkettenis 	struct device		*child;
188f33bf8ceSkettenis 	cfprint_t		 print = NULL;
189f33bf8ceSkettenis 
190f33bf8ceSkettenis 	/* Skip if already attached early. */
191f33bf8ceSkettenis 	for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
192f33bf8ceSkettenis 		if (sc->sc_early_nodes[i] == node)
193f33bf8ceSkettenis 			return;
194f33bf8ceSkettenis 		if (sc->sc_early_nodes[i] == 0)
195f33bf8ceSkettenis 			break;
196f33bf8ceSkettenis 	}
197baed8f06Sdrahn 
198baed8f06Sdrahn 	memset(&fa, 0, sizeof(fa));
199baed8f06Sdrahn 	fa.fa_name = "";
200baed8f06Sdrahn 	fa.fa_node = node;
201baed8f06Sdrahn 	fa.fa_iot = sc->sc_iot;
202baed8f06Sdrahn 	fa.fa_dmat = sc->sc_dmat;
203baed8f06Sdrahn 	fa.fa_acells = sc->sc_acells;
204baed8f06Sdrahn 	fa.fa_scells = sc->sc_scells;
205baed8f06Sdrahn 
206baed8f06Sdrahn 	len = OF_getproplen(node, "reg");
207baed8f06Sdrahn 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
208f33bf8ceSkettenis 	if (len > 0 && (len % line) == 0) {
209baed8f06Sdrahn 		reg = malloc(len, M_TEMP, M_WAITOK);
210baed8f06Sdrahn 		OF_getpropintarray(node, "reg", reg, len);
211baed8f06Sdrahn 
212baed8f06Sdrahn 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
213baed8f06Sdrahn 		    M_DEVBUF, M_WAITOK);
214baed8f06Sdrahn 		fa.fa_nreg = (len / line);
215baed8f06Sdrahn 
216baed8f06Sdrahn 		for (i = 0, cell = reg; i < len / line; i++) {
217baed8f06Sdrahn 			if (sc->sc_acells >= 1)
218baed8f06Sdrahn 				fa.fa_reg[i].addr = cell[0];
219baed8f06Sdrahn 			if (sc->sc_acells == 2) {
220baed8f06Sdrahn 				fa.fa_reg[i].addr <<= 32;
221baed8f06Sdrahn 				fa.fa_reg[i].addr |= cell[1];
222baed8f06Sdrahn 			}
223baed8f06Sdrahn 			cell += sc->sc_acells;
224baed8f06Sdrahn 			if (sc->sc_scells >= 1)
225baed8f06Sdrahn 				fa.fa_reg[i].size = cell[0];
226baed8f06Sdrahn 			if (sc->sc_scells == 2) {
227baed8f06Sdrahn 				fa.fa_reg[i].size <<= 32;
228baed8f06Sdrahn 				fa.fa_reg[i].size |= cell[1];
229baed8f06Sdrahn 			}
230baed8f06Sdrahn 			cell += sc->sc_scells;
231baed8f06Sdrahn 		}
232baed8f06Sdrahn 
233baed8f06Sdrahn 		free(reg, M_TEMP, len);
234baed8f06Sdrahn 	}
235baed8f06Sdrahn 
236baed8f06Sdrahn 	len = OF_getproplen(node, "interrupts");
237baed8f06Sdrahn 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
238baed8f06Sdrahn 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
239baed8f06Sdrahn 		fa.fa_nintr = len / sizeof(uint32_t);
240baed8f06Sdrahn 
241baed8f06Sdrahn 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
242baed8f06Sdrahn 	}
243baed8f06Sdrahn 
24416c51e1aSkettenis 	if (OF_getproplen(node, "dma-noncoherent") >= 0) {
24516c51e1aSkettenis 		fa.fa_dmat = malloc(sizeof(*sc->sc_dmat),
24616c51e1aSkettenis 		    M_DEVBUF, M_WAITOK | M_ZERO);
24716c51e1aSkettenis 		memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat));
24816c51e1aSkettenis 		fa.fa_dmat->_flags &= ~BUS_DMA_COHERENT;
24916c51e1aSkettenis 	} else if (OF_getproplen(node, "dma-coherent") >= 0) {
25016c51e1aSkettenis 		fa.fa_dmat = malloc(sizeof(*sc->sc_dmat),
25116c51e1aSkettenis 		    M_DEVBUF, M_WAITOK | M_ZERO);
25216c51e1aSkettenis 		memcpy(fa.fa_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat));
25316c51e1aSkettenis 		fa.fa_dmat->_flags |= BUS_DMA_COHERENT;
25416c51e1aSkettenis 	}
25516c51e1aSkettenis 
256f33bf8ceSkettenis 	if (submatch == NULL && sc->sc_early == 0)
257f33bf8ceSkettenis 		print = mainbus_print;
258baed8f06Sdrahn 	if (submatch == NULL)
259baed8f06Sdrahn 		submatch = mainbus_match_status;
260baed8f06Sdrahn 
261f33bf8ceSkettenis 	child = config_found_sm(self, &fa, print, submatch);
262baed8f06Sdrahn 
263f33bf8ceSkettenis 	/* Record nodes that we attach early. */
264f33bf8ceSkettenis 	if (child && sc->sc_early) {
265f33bf8ceSkettenis 		for (i = 0; i < nitems(sc->sc_early_nodes); i++) {
266f33bf8ceSkettenis 			if (sc->sc_early_nodes[i] != 0)
267f33bf8ceSkettenis 				continue;
268f33bf8ceSkettenis 			sc->sc_early_nodes[i] = node;
269f33bf8ceSkettenis 			break;
270f33bf8ceSkettenis 		}
271f33bf8ceSkettenis 	}
272baed8f06Sdrahn 
273baed8f06Sdrahn 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
274baed8f06Sdrahn 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
275baed8f06Sdrahn }
276baed8f06Sdrahn 
277baed8f06Sdrahn int
278baed8f06Sdrahn mainbus_match_status(struct device *parent, void *match, void *aux)
279baed8f06Sdrahn {
280baed8f06Sdrahn 	struct mainbus_softc *sc = (struct mainbus_softc *)parent;
281baed8f06Sdrahn 	struct fdt_attach_args *fa = aux;
282baed8f06Sdrahn 	struct cfdata *cf = match;
283baed8f06Sdrahn 	char buf[32];
284baed8f06Sdrahn 
285baed8f06Sdrahn 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
286baed8f06Sdrahn 	    strcmp(buf, "disabled") == 0)
287baed8f06Sdrahn 		return 0;
288baed8f06Sdrahn 
289baed8f06Sdrahn 	if (cf->cf_loc[0] == sc->sc_early)
290baed8f06Sdrahn 		return (*cf->cf_attach->ca_match)(parent, match, aux);
291f33bf8ceSkettenis 
292baed8f06Sdrahn 	return 0;
293baed8f06Sdrahn }
294baed8f06Sdrahn 
295baed8f06Sdrahn void
296baed8f06Sdrahn mainbus_attach_cpus(struct device *self, cfmatch_t match)
297baed8f06Sdrahn {
298baed8f06Sdrahn 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
299baed8f06Sdrahn 	int node = OF_finddevice("/cpus");
300baed8f06Sdrahn 	int acells, scells;
301baed8f06Sdrahn 	char buf[32];
302baed8f06Sdrahn 
303f33bf8ceSkettenis 	if (node == -1)
304baed8f06Sdrahn 		return;
305baed8f06Sdrahn 
306baed8f06Sdrahn 	acells = sc->sc_acells;
307baed8f06Sdrahn 	scells = sc->sc_scells;
308baed8f06Sdrahn 	sc->sc_acells = OF_getpropint(node, "#address-cells", 2);
309baed8f06Sdrahn 	sc->sc_scells = OF_getpropint(node, "#size-cells", 0);
310baed8f06Sdrahn 
311baed8f06Sdrahn 	ncpusfound = 0;
312baed8f06Sdrahn 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
313b4ec57b8Sderaadt 		if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 &&
314b4ec57b8Sderaadt 		    strcmp(buf, "disabled") == 0)
315b4ec57b8Sderaadt 			continue;
316b4ec57b8Sderaadt 
317baed8f06Sdrahn 		if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 &&
318baed8f06Sdrahn 		    strcmp(buf, "cpu") == 0)
319baed8f06Sdrahn 			ncpusfound++;
320baed8f06Sdrahn 
321baed8f06Sdrahn 		mainbus_attach_node(self, node, match);
322baed8f06Sdrahn 	}
323baed8f06Sdrahn 
324baed8f06Sdrahn 	sc->sc_acells = acells;
325baed8f06Sdrahn 	sc->sc_scells = scells;
326baed8f06Sdrahn }
327baed8f06Sdrahn 
328baed8f06Sdrahn int
329baed8f06Sdrahn mainbus_match_primary(struct device *parent, void *match, void *aux)
330baed8f06Sdrahn {
331baed8f06Sdrahn 	struct fdt_attach_args *fa = aux;
332baed8f06Sdrahn 	struct cfdata *cf = match;
333baed8f06Sdrahn 
334862607c0Sjsg 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != boot_hart)
335baed8f06Sdrahn 		return 0;
336baed8f06Sdrahn 
337baed8f06Sdrahn 	return (*cf->cf_attach->ca_match)(parent, match, aux);
338baed8f06Sdrahn }
339baed8f06Sdrahn 
340baed8f06Sdrahn int
341baed8f06Sdrahn mainbus_match_secondary(struct device *parent, void *match, void *aux)
342baed8f06Sdrahn {
343baed8f06Sdrahn 	struct fdt_attach_args *fa = aux;
344baed8f06Sdrahn 	struct cfdata *cf = match;
345baed8f06Sdrahn 
346f561954dSkettenis 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == boot_hart)
347baed8f06Sdrahn 		return 0;
348baed8f06Sdrahn 
349baed8f06Sdrahn 	return (*cf->cf_attach->ca_match)(parent, match, aux);
350baed8f06Sdrahn }
351baed8f06Sdrahn 
352baed8f06Sdrahn void
353baed8f06Sdrahn mainbus_attach_framebuffer(struct device *self)
354baed8f06Sdrahn {
355baed8f06Sdrahn 	int node = OF_finddevice("/chosen");
356baed8f06Sdrahn 
357f33bf8ceSkettenis 	if (node == -1)
358baed8f06Sdrahn 		return;
359baed8f06Sdrahn 
360baed8f06Sdrahn 	for (node = OF_child(node); node != 0; node = OF_peer(node))
361baed8f06Sdrahn 		mainbus_attach_node(self, node, NULL);
362baed8f06Sdrahn }
363