xref: /openbsd-src/sys/arch/arm/mainbus/mainbus.c (revision 130ea1eca9fa3e4fe9bd0c779b913a2bb61dee85)
1*130ea1ecSderaadt /* $OpenBSD: mainbus.c,v 1.25 2024/08/18 15:50:49 deraadt Exp $ */
2e1e4f5b1Sdrahn /*
379645871Spatrick  * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se>
488a615dbSkettenis  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
5e1e4f5b1Sdrahn  *
679645871Spatrick  * Permission to use, copy, modify, and distribute this software for any
779645871Spatrick  * purpose with or without fee is hereby granted, provided that the above
879645871Spatrick  * copyright notice and this permission notice appear in all copies.
9e1e4f5b1Sdrahn  *
1079645871Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1179645871Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1279645871Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1379645871Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1479645871Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1579645871Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1679645871Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17e1e4f5b1Sdrahn  */
18e1e4f5b1Sdrahn 
19e1e4f5b1Sdrahn #include <sys/param.h>
20e1e4f5b1Sdrahn #include <sys/systm.h>
21e1e4f5b1Sdrahn #include <sys/kernel.h>
22e1e4f5b1Sdrahn #include <sys/device.h>
233dfd0072Sjsg #include <sys/malloc.h>
24e1e4f5b1Sdrahn 
2579645871Spatrick #include <dev/ofw/openfirm.h>
26df9226ecSjsg #include <dev/ofw/fdt.h>
2720584b0dSkettenis #include <dev/ofw/ofw_thermal.h>
2879645871Spatrick 
29e1e4f5b1Sdrahn #include <arm/mainbus/mainbus.h>
30e1e4f5b1Sdrahn 
3179645871Spatrick int mainbus_match(struct device *, void *, void *);
3279645871Spatrick void mainbus_attach(struct device *, struct device *, void *);
33e1e4f5b1Sdrahn 
3488a615dbSkettenis void mainbus_attach_node(struct device *, int, cfmatch_t);
3588a615dbSkettenis int mainbus_match_status(struct device *, void *, void *);
3688a615dbSkettenis void mainbus_attach_cpus(struct device *, cfmatch_t);
3788a615dbSkettenis int mainbus_match_primary(struct device *, void *, void *);
3888a615dbSkettenis int mainbus_match_secondary(struct device *, void *, void *);
396fd46d03Skettenis void mainbus_attach_framebuffer(struct device *);
40e1e4f5b1Sdrahn 
4179645871Spatrick struct mainbus_softc {
4279645871Spatrick 	struct device		 sc_dev;
43b70d681bSkettenis 	int			 sc_node;
4479645871Spatrick 	bus_space_tag_t		 sc_iot;
4579645871Spatrick 	bus_dma_tag_t		 sc_dmat;
465586125eSpatrick 	int			 sc_acells;
475586125eSpatrick 	int			 sc_scells;
48df9226ecSjsg 	int			*sc_ranges;
49df9226ecSjsg 	int			 sc_rangeslen;
50b70d681bSkettenis 	int			 sc_early;
5179645871Spatrick };
52e1e4f5b1Sdrahn 
53e3ee5e84Smpi const struct cfattach mainbus_ca = {
54*130ea1ecSderaadt 	sizeof(struct mainbus_softc), mainbus_match, mainbus_attach
55e1e4f5b1Sdrahn };
56e1e4f5b1Sdrahn 
57e1e4f5b1Sdrahn struct cfdriver mainbus_cd = {
58e1e4f5b1Sdrahn 	NULL, "mainbus", DV_DULL
59e1e4f5b1Sdrahn };
60e1e4f5b1Sdrahn 
6179645871Spatrick struct arm32_bus_dma_tag mainbus_dma_tag = {
6279645871Spatrick 	NULL,
6379645871Spatrick 	_bus_dmamap_create,
6479645871Spatrick 	_bus_dmamap_destroy,
6579645871Spatrick 	_bus_dmamap_load,
6679645871Spatrick 	_bus_dmamap_load_mbuf,
6779645871Spatrick 	_bus_dmamap_load_uio,
6879645871Spatrick 	_bus_dmamap_load_raw,
6937084e1eSkettenis 	_bus_dmamap_load_buffer,
7079645871Spatrick 	_bus_dmamap_unload,
7179645871Spatrick 	_bus_dmamap_sync,
7279645871Spatrick 	_bus_dmamem_alloc,
7379645871Spatrick 	_bus_dmamem_free,
7479645871Spatrick 	_bus_dmamem_map,
7579645871Spatrick 	_bus_dmamem_unmap,
7679645871Spatrick 	_bus_dmamem_mmap,
7779645871Spatrick };
78e1e4f5b1Sdrahn 
7979645871Spatrick /*
8079645871Spatrick  * Mainbus takes care of FDT and non-FDT machines, so we
8179645871Spatrick  * always attach.
8279645871Spatrick  */
83e1e4f5b1Sdrahn int
8479645871Spatrick mainbus_match(struct device *parent, void *cfdata, void *aux)
85e1e4f5b1Sdrahn {
86e1e4f5b1Sdrahn 	return (1);
87e1e4f5b1Sdrahn }
88e1e4f5b1Sdrahn 
8904e4a795Sjsg extern struct bus_space armv7_bs_tag;
9004e4a795Sjsg void platform_init_mainbus(struct device *);
913dfd0072Sjsg 
92e1e4f5b1Sdrahn void
9379645871Spatrick mainbus_attach(struct device *parent, struct device *self, void *aux)
94e1e4f5b1Sdrahn {
9579645871Spatrick 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
96aaf7ae02Spatrick 	char prop[128];
973dfd0072Sjsg 	int node, len;
98e1e4f5b1Sdrahn 
993a2a51e5Skettenis 	arm_intr_init_fdt();
1003a2a51e5Skettenis 
101b70d681bSkettenis 	sc->sc_node = OF_peer(0);
10279645871Spatrick 	sc->sc_iot = &armv7_bs_tag;
10379645871Spatrick 	sc->sc_dmat = &mainbus_dma_tag;
1045586125eSpatrick 	sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1);
1055586125eSpatrick 	sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1);
106cc7b0317Sdrahn 
107aaf7ae02Spatrick 	len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop));
108b70d681bSkettenis 	if (len > 0) {
109aaf7ae02Spatrick 		printf(": %s\n", prop);
1103dfd0072Sjsg 		hw_prod = malloc(len, M_DEVBUF, M_NOWAIT);
1113dfd0072Sjsg 		if (hw_prod)
112aaf7ae02Spatrick 			strlcpy(hw_prod, prop, len);
1133dfd0072Sjsg 	} else
11479645871Spatrick 		printf(": unknown model\n");
11579645871Spatrick 
116aaf7ae02Spatrick 	len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop));
117aaf7ae02Spatrick 	if (len > 0) {
118aaf7ae02Spatrick 		hw_serial = malloc(len, M_DEVBUF, M_NOWAIT);
119aaf7ae02Spatrick 		if (hw_serial)
120aaf7ae02Spatrick 			strlcpy(hw_serial, prop, len);
121aaf7ae02Spatrick 	}
122aaf7ae02Spatrick 
12388a615dbSkettenis 	/* Attach primary CPU first. */
12488a615dbSkettenis 	mainbus_attach_cpus(self, mainbus_match_primary);
12579645871Spatrick 
12688a615dbSkettenis 	platform_init_mainbus(self);
12779645871Spatrick 
128df9226ecSjsg 	sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges");
129df9226ecSjsg 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
130df9226ecSjsg 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
131df9226ecSjsg 		OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges,
132df9226ecSjsg 		    sc->sc_rangeslen);
133df9226ecSjsg 	}
134df9226ecSjsg 
13579645871Spatrick 	/* Scan the whole tree. */
136b70d681bSkettenis 	sc->sc_early = 1;
137b70d681bSkettenis 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
138b70d681bSkettenis 		mainbus_attach_node(self, node, NULL);
139b70d681bSkettenis 
140b70d681bSkettenis 	sc->sc_early = 0;
141b70d681bSkettenis 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
14288a615dbSkettenis 		mainbus_attach_node(self, node, NULL);
1436fd46d03Skettenis 
1446fd46d03Skettenis 	mainbus_attach_framebuffer(self);
14588a615dbSkettenis 
14688a615dbSkettenis 	/* Attach secondary CPUs. */
14788a615dbSkettenis 	mainbus_attach_cpus(self, mainbus_match_secondary);
14820584b0dSkettenis 
14920584b0dSkettenis 	thermal_init();
15079645871Spatrick }
15179645871Spatrick 
15279645871Spatrick /*
15379645871Spatrick  * Look for a driver that wants to be attached to this node.
15479645871Spatrick  */
15579645871Spatrick void
15688a615dbSkettenis mainbus_attach_node(struct device *self, int node, cfmatch_t submatch)
15779645871Spatrick {
15879645871Spatrick 	struct mainbus_softc	*sc = (struct mainbus_softc *)self;
15979645871Spatrick 	struct fdt_attach_args	 fa;
160df9226ecSjsg 	int			 i, len, line;
161df9226ecSjsg 	uint32_t		*cell, *reg;
16279645871Spatrick 
16379645871Spatrick 	memset(&fa, 0, sizeof(fa));
16479645871Spatrick 	fa.fa_name = "";
16579645871Spatrick 	fa.fa_node = node;
16679645871Spatrick 	fa.fa_iot = sc->sc_iot;
16779645871Spatrick 	fa.fa_dmat = sc->sc_dmat;
1685586125eSpatrick 	fa.fa_acells = sc->sc_acells;
1695586125eSpatrick 	fa.fa_scells = sc->sc_scells;
17079645871Spatrick 
171df9226ecSjsg 	len = OF_getproplen(node, "reg");
172df9226ecSjsg 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
173df9226ecSjsg 	if (len > 0 && (len % line) == 0) {
174df9226ecSjsg 		reg = malloc(len, M_TEMP, M_WAITOK);
175df9226ecSjsg 		OF_getpropintarray(node, "reg", reg, len);
176df9226ecSjsg 
177df9226ecSjsg 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
178df9226ecSjsg 		    M_DEVBUF, M_WAITOK);
179df9226ecSjsg 		fa.fa_nreg = (len / line);
180df9226ecSjsg 
181df9226ecSjsg 		for (i = 0, cell = reg; i < len / line; i++) {
182df9226ecSjsg 			if (sc->sc_acells >= 1)
183df9226ecSjsg 				fa.fa_reg[i].addr = cell[0];
184df9226ecSjsg 			if (sc->sc_acells == 2) {
185df9226ecSjsg 				fa.fa_reg[i].addr <<= 32;
186df9226ecSjsg 				fa.fa_reg[i].addr |= cell[1];
187df9226ecSjsg 			}
188df9226ecSjsg 			cell += sc->sc_acells;
189df9226ecSjsg 			if (sc->sc_scells >= 1)
190df9226ecSjsg 				fa.fa_reg[i].size = cell[0];
191df9226ecSjsg 			if (sc->sc_scells == 2) {
192df9226ecSjsg 				fa.fa_reg[i].size <<= 32;
193df9226ecSjsg 				fa.fa_reg[i].size |= cell[1];
194df9226ecSjsg 			}
195df9226ecSjsg 			cell += sc->sc_scells;
196df9226ecSjsg 		}
197df9226ecSjsg 
198df9226ecSjsg 		free(reg, M_TEMP, len);
199df9226ecSjsg 	}
200df9226ecSjsg 
201df9226ecSjsg 	len = OF_getproplen(node, "interrupts");
202df9226ecSjsg 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
203df9226ecSjsg 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
204df9226ecSjsg 		fa.fa_nintr = len / sizeof(uint32_t);
205df9226ecSjsg 
206df9226ecSjsg 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
207df9226ecSjsg 	}
208df9226ecSjsg 
20988a615dbSkettenis 	if (submatch == NULL)
21088a615dbSkettenis 		submatch = mainbus_match_status;
21188a615dbSkettenis 	config_found_sm(self, &fa, NULL, submatch);
212df9226ecSjsg 
213df9226ecSjsg 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
214df9226ecSjsg 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
21579645871Spatrick }
21679645871Spatrick 
21788a615dbSkettenis int
21888a615dbSkettenis mainbus_match_status(struct device *parent, void *match, void *aux)
21988a615dbSkettenis {
220b70d681bSkettenis 	struct mainbus_softc *sc = (struct mainbus_softc *)parent;
22188a615dbSkettenis 	struct fdt_attach_args *fa = aux;
22288a615dbSkettenis 	struct cfdata *cf = match;
22388a615dbSkettenis 	char buf[32];
22488a615dbSkettenis 
22588a615dbSkettenis 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
22688a615dbSkettenis 	    strcmp(buf, "disabled") == 0)
22788a615dbSkettenis 		return 0;
22888a615dbSkettenis 
229b70d681bSkettenis 	if (cf->cf_loc[0] == sc->sc_early)
23088a615dbSkettenis 		return (*cf->cf_attach->ca_match)(parent, match, aux);
231b70d681bSkettenis 	return 0;
23288a615dbSkettenis }
23388a615dbSkettenis 
23488a615dbSkettenis void
23588a615dbSkettenis mainbus_attach_cpus(struct device *self, cfmatch_t match)
23688a615dbSkettenis {
23788a615dbSkettenis 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
23888a615dbSkettenis 	int node = OF_finddevice("/cpus");
23988a615dbSkettenis 	int acells, scells;
24088a615dbSkettenis 
24188a615dbSkettenis 	if (node == 0)
24288a615dbSkettenis 		return;
24388a615dbSkettenis 
24488a615dbSkettenis 	acells = sc->sc_acells;
24588a615dbSkettenis 	scells = sc->sc_scells;
24688a615dbSkettenis 	sc->sc_acells = OF_getpropint(node, "#address-cells", 1);
24788a615dbSkettenis 	sc->sc_scells = OF_getpropint(node, "#size-cells", 0);
24888a615dbSkettenis 
24988a615dbSkettenis 	for (node = OF_child(node); node != 0; node = OF_peer(node))
25088a615dbSkettenis 		mainbus_attach_node(self, node, match);
25188a615dbSkettenis 
25288a615dbSkettenis 	sc->sc_acells = acells;
25388a615dbSkettenis 	sc->sc_scells = scells;
25488a615dbSkettenis }
25588a615dbSkettenis 
25688a615dbSkettenis int
25788a615dbSkettenis mainbus_match_primary(struct device *parent, void *match, void *aux)
25888a615dbSkettenis {
25988a615dbSkettenis 	struct fdt_attach_args *fa = aux;
26088a615dbSkettenis 	struct cfdata *cf = match;
26188a615dbSkettenis 	uint32_t mpidr;
26288a615dbSkettenis 
26388a615dbSkettenis 	__asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr));
26488a615dbSkettenis 
26588a615dbSkettenis 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF))
26688a615dbSkettenis 		return 0;
26788a615dbSkettenis 
26888a615dbSkettenis 	return (*cf->cf_attach->ca_match)(parent, match, aux);
26988a615dbSkettenis }
27088a615dbSkettenis 
27188a615dbSkettenis int
27288a615dbSkettenis mainbus_match_secondary(struct device *parent, void *match, void *aux)
27388a615dbSkettenis {
27488a615dbSkettenis 	struct fdt_attach_args *fa = aux;
27588a615dbSkettenis 	struct cfdata *cf = match;
27688a615dbSkettenis 	uint32_t mpidr;
27788a615dbSkettenis 
27888a615dbSkettenis 	__asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr));
27988a615dbSkettenis 
28088a615dbSkettenis 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF))
28188a615dbSkettenis 		return 0;
28288a615dbSkettenis 
28388a615dbSkettenis 	return (*cf->cf_attach->ca_match)(parent, match, aux);
28488a615dbSkettenis }
28588a615dbSkettenis 
2866fd46d03Skettenis void
2876fd46d03Skettenis mainbus_attach_framebuffer(struct device *self)
2886fd46d03Skettenis {
2896fd46d03Skettenis 	int node = OF_finddevice("/chosen");
2906fd46d03Skettenis 
2916fd46d03Skettenis 	if (node == 0)
2926fd46d03Skettenis 		return;
2936fd46d03Skettenis 
2946fd46d03Skettenis 	for (node = OF_child(node); node != 0; node = OF_peer(node))
29588a615dbSkettenis 		mainbus_attach_node(self, node, NULL);
2966fd46d03Skettenis }
2976fd46d03Skettenis 
29879645871Spatrick /*
29988a615dbSkettenis  * Legacy support for SoCs that do not fully use FDT.
30079645871Spatrick  */
30179645871Spatrick void
30279645871Spatrick mainbus_legacy_found(struct device *self, char *name)
303e1e4f5b1Sdrahn {
30479645871Spatrick 	union mainbus_attach_args ma;
305afed0fbfSmiod 
30679645871Spatrick 	memset(&ma, 0, sizeof(ma));
30779645871Spatrick 	ma.ma_name = name;
308afed0fbfSmiod 
30979645871Spatrick 	config_found(self, &ma, NULL);
310e1e4f5b1Sdrahn }
311