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