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