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