xref: /openbsd/sys/arch/arm/mainbus/mainbus.c (revision 130ea1ec)
1 /* $OpenBSD: mainbus.c,v 1.25 2024/08/18 15:50:49 deraadt 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 <dev/ofw/openfirm.h>
26 #include <dev/ofw/fdt.h>
27 #include <dev/ofw/ofw_thermal.h>
28 
29 #include <arm/mainbus/mainbus.h>
30 
31 int mainbus_match(struct device *, void *, void *);
32 void mainbus_attach(struct device *, struct device *, void *);
33 
34 void mainbus_attach_node(struct device *, int, cfmatch_t);
35 int mainbus_match_status(struct device *, void *, void *);
36 void mainbus_attach_cpus(struct device *, cfmatch_t);
37 int mainbus_match_primary(struct device *, void *, void *);
38 int mainbus_match_secondary(struct device *, void *, void *);
39 void mainbus_attach_framebuffer(struct device *);
40 
41 struct mainbus_softc {
42 	struct device		 sc_dev;
43 	int			 sc_node;
44 	bus_space_tag_t		 sc_iot;
45 	bus_dma_tag_t		 sc_dmat;
46 	int			 sc_acells;
47 	int			 sc_scells;
48 	int			*sc_ranges;
49 	int			 sc_rangeslen;
50 	int			 sc_early;
51 };
52 
53 const struct cfattach mainbus_ca = {
54 	sizeof(struct mainbus_softc), mainbus_match, mainbus_attach
55 };
56 
57 struct cfdriver mainbus_cd = {
58 	NULL, "mainbus", DV_DULL
59 };
60 
61 struct arm32_bus_dma_tag mainbus_dma_tag = {
62 	NULL,
63 	_bus_dmamap_create,
64 	_bus_dmamap_destroy,
65 	_bus_dmamap_load,
66 	_bus_dmamap_load_mbuf,
67 	_bus_dmamap_load_uio,
68 	_bus_dmamap_load_raw,
69 	_bus_dmamap_load_buffer,
70 	_bus_dmamap_unload,
71 	_bus_dmamap_sync,
72 	_bus_dmamem_alloc,
73 	_bus_dmamem_free,
74 	_bus_dmamem_map,
75 	_bus_dmamem_unmap,
76 	_bus_dmamem_mmap,
77 };
78 
79 /*
80  * Mainbus takes care of FDT and non-FDT machines, so we
81  * always attach.
82  */
83 int
mainbus_match(struct device * parent,void * cfdata,void * aux)84 mainbus_match(struct device *parent, void *cfdata, void *aux)
85 {
86 	return (1);
87 }
88 
89 extern struct bus_space armv7_bs_tag;
90 void platform_init_mainbus(struct device *);
91 
92 void
mainbus_attach(struct device * parent,struct device * self,void * aux)93 mainbus_attach(struct device *parent, struct device *self, void *aux)
94 {
95 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
96 	char prop[128];
97 	int node, len;
98 
99 	arm_intr_init_fdt();
100 
101 	sc->sc_node = OF_peer(0);
102 	sc->sc_iot = &armv7_bs_tag;
103 	sc->sc_dmat = &mainbus_dma_tag;
104 	sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1);
105 	sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1);
106 
107 	len = OF_getprop(sc->sc_node, "model", prop, sizeof(prop));
108 	if (len > 0) {
109 		printf(": %s\n", prop);
110 		hw_prod = malloc(len, M_DEVBUF, M_NOWAIT);
111 		if (hw_prod)
112 			strlcpy(hw_prod, prop, len);
113 	} else
114 		printf(": unknown model\n");
115 
116 	len = OF_getprop(sc->sc_node, "serial-number", prop, sizeof(prop));
117 	if (len > 0) {
118 		hw_serial = malloc(len, M_DEVBUF, M_NOWAIT);
119 		if (hw_serial)
120 			strlcpy(hw_serial, prop, len);
121 	}
122 
123 	/* Attach primary CPU first. */
124 	mainbus_attach_cpus(self, mainbus_match_primary);
125 
126 	platform_init_mainbus(self);
127 
128 	sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges");
129 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
130 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
131 		OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges,
132 		    sc->sc_rangeslen);
133 	}
134 
135 	/* Scan the whole tree. */
136 	sc->sc_early = 1;
137 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
138 		mainbus_attach_node(self, node, NULL);
139 
140 	sc->sc_early = 0;
141 	for (node = OF_child(sc->sc_node); node != 0; node = OF_peer(node))
142 		mainbus_attach_node(self, node, NULL);
143 
144 	mainbus_attach_framebuffer(self);
145 
146 	/* Attach secondary CPUs. */
147 	mainbus_attach_cpus(self, mainbus_match_secondary);
148 
149 	thermal_init();
150 }
151 
152 /*
153  * Look for a driver that wants to be attached to this node.
154  */
155 void
mainbus_attach_node(struct device * self,int node,cfmatch_t submatch)156 mainbus_attach_node(struct device *self, int node, cfmatch_t submatch)
157 {
158 	struct mainbus_softc	*sc = (struct mainbus_softc *)self;
159 	struct fdt_attach_args	 fa;
160 	int			 i, len, line;
161 	uint32_t		*cell, *reg;
162 
163 	memset(&fa, 0, sizeof(fa));
164 	fa.fa_name = "";
165 	fa.fa_node = node;
166 	fa.fa_iot = sc->sc_iot;
167 	fa.fa_dmat = sc->sc_dmat;
168 	fa.fa_acells = sc->sc_acells;
169 	fa.fa_scells = sc->sc_scells;
170 
171 	len = OF_getproplen(node, "reg");
172 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
173 	if (len > 0 && (len % line) == 0) {
174 		reg = malloc(len, M_TEMP, M_WAITOK);
175 		OF_getpropintarray(node, "reg", reg, len);
176 
177 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
178 		    M_DEVBUF, M_WAITOK);
179 		fa.fa_nreg = (len / line);
180 
181 		for (i = 0, cell = reg; i < len / line; i++) {
182 			if (sc->sc_acells >= 1)
183 				fa.fa_reg[i].addr = cell[0];
184 			if (sc->sc_acells == 2) {
185 				fa.fa_reg[i].addr <<= 32;
186 				fa.fa_reg[i].addr |= cell[1];
187 			}
188 			cell += sc->sc_acells;
189 			if (sc->sc_scells >= 1)
190 				fa.fa_reg[i].size = cell[0];
191 			if (sc->sc_scells == 2) {
192 				fa.fa_reg[i].size <<= 32;
193 				fa.fa_reg[i].size |= cell[1];
194 			}
195 			cell += sc->sc_scells;
196 		}
197 
198 		free(reg, M_TEMP, len);
199 	}
200 
201 	len = OF_getproplen(node, "interrupts");
202 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
203 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
204 		fa.fa_nintr = len / sizeof(uint32_t);
205 
206 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
207 	}
208 
209 	if (submatch == NULL)
210 		submatch = mainbus_match_status;
211 	config_found_sm(self, &fa, NULL, submatch);
212 
213 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
214 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
215 }
216 
217 int
mainbus_match_status(struct device * parent,void * match,void * aux)218 mainbus_match_status(struct device *parent, void *match, void *aux)
219 {
220 	struct mainbus_softc *sc = (struct mainbus_softc *)parent;
221 	struct fdt_attach_args *fa = aux;
222 	struct cfdata *cf = match;
223 	char buf[32];
224 
225 	if (OF_getprop(fa->fa_node, "status", buf, sizeof(buf)) > 0 &&
226 	    strcmp(buf, "disabled") == 0)
227 		return 0;
228 
229 	if (cf->cf_loc[0] == sc->sc_early)
230 		return (*cf->cf_attach->ca_match)(parent, match, aux);
231 	return 0;
232 }
233 
234 void
mainbus_attach_cpus(struct device * self,cfmatch_t match)235 mainbus_attach_cpus(struct device *self, cfmatch_t match)
236 {
237 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
238 	int node = OF_finddevice("/cpus");
239 	int acells, scells;
240 
241 	if (node == 0)
242 		return;
243 
244 	acells = sc->sc_acells;
245 	scells = sc->sc_scells;
246 	sc->sc_acells = OF_getpropint(node, "#address-cells", 1);
247 	sc->sc_scells = OF_getpropint(node, "#size-cells", 0);
248 
249 	for (node = OF_child(node); node != 0; node = OF_peer(node))
250 		mainbus_attach_node(self, node, match);
251 
252 	sc->sc_acells = acells;
253 	sc->sc_scells = scells;
254 }
255 
256 int
mainbus_match_primary(struct device * parent,void * match,void * aux)257 mainbus_match_primary(struct device *parent, void *match, void *aux)
258 {
259 	struct fdt_attach_args *fa = aux;
260 	struct cfdata *cf = match;
261 	uint32_t mpidr;
262 
263 	__asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr));
264 
265 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr != (mpidr & MPIDR_AFF))
266 		return 0;
267 
268 	return (*cf->cf_attach->ca_match)(parent, match, aux);
269 }
270 
271 int
mainbus_match_secondary(struct device * parent,void * match,void * aux)272 mainbus_match_secondary(struct device *parent, void *match, void *aux)
273 {
274 	struct fdt_attach_args *fa = aux;
275 	struct cfdata *cf = match;
276 	uint32_t mpidr;
277 
278 	__asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (mpidr));
279 
280 	if (fa->fa_nreg < 1 || fa->fa_reg[0].addr == (mpidr & MPIDR_AFF))
281 		return 0;
282 
283 	return (*cf->cf_attach->ca_match)(parent, match, aux);
284 }
285 
286 void
mainbus_attach_framebuffer(struct device * self)287 mainbus_attach_framebuffer(struct device *self)
288 {
289 	int node = OF_finddevice("/chosen");
290 
291 	if (node == 0)
292 		return;
293 
294 	for (node = OF_child(node); node != 0; node = OF_peer(node))
295 		mainbus_attach_node(self, node, NULL);
296 }
297 
298 /*
299  * Legacy support for SoCs that do not fully use FDT.
300  */
301 void
mainbus_legacy_found(struct device * self,char * name)302 mainbus_legacy_found(struct device *self, char *name)
303 {
304 	union mainbus_attach_args ma;
305 
306 	memset(&ma, 0, sizeof(ma));
307 	ma.ma_name = name;
308 
309 	config_found(self, &ma, NULL);
310 }
311