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