xref: /freebsd/sys/dev/fdt/simplebus.c (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 Nathan Whitehorn
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/module.h>
33 #include <sys/bus.h>
34 #include <sys/conf.h>
35 #include <sys/kernel.h>
36 #include <sys/rman.h>
37 
38 #include <dev/ofw/openfirm.h>
39 #include <dev/ofw/ofw_bus.h>
40 #include <dev/ofw/ofw_bus_subr.h>
41 
42 #include <dev/fdt/simplebus.h>
43 
44 /*
45  * Bus interface.
46  */
47 static int		simplebus_probe(device_t dev);
48 static struct resource *simplebus_alloc_resource(device_t, device_t, int,
49     int *, rman_res_t, rman_res_t, rman_res_t, u_int);
50 static void		simplebus_probe_nomatch(device_t bus, device_t child);
51 static int		simplebus_print_child(device_t bus, device_t child);
52 static device_t		simplebus_add_child(device_t dev, u_int order,
53     const char *name, int unit);
54 static struct resource_list *simplebus_get_resource_list(device_t bus,
55     device_t child);
56 
57 static ssize_t		simplebus_get_property(device_t bus, device_t child,
58     const char *propname, void *propvalue, size_t size,
59     device_property_type_t type);
60 /*
61  * ofw_bus interface
62  */
63 static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus,
64     device_t child);
65 
66 /*
67  * Driver methods.
68  */
69 static device_method_t	simplebus_methods[] = {
70 	/* Device interface */
71 	DEVMETHOD(device_probe,		simplebus_probe),
72 	DEVMETHOD(device_attach,	simplebus_attach),
73 	DEVMETHOD(device_detach,	simplebus_detach),
74 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
75 	DEVMETHOD(device_suspend,	bus_generic_suspend),
76 	DEVMETHOD(device_resume,	bus_generic_resume),
77 
78 	/* Bus interface */
79 	DEVMETHOD(bus_add_child,	simplebus_add_child),
80 	DEVMETHOD(bus_print_child,	simplebus_print_child),
81 	DEVMETHOD(bus_probe_nomatch,	simplebus_probe_nomatch),
82 	DEVMETHOD(bus_read_ivar,	bus_generic_read_ivar),
83 	DEVMETHOD(bus_write_ivar,	bus_generic_write_ivar),
84 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
85 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
86 	DEVMETHOD(bus_alloc_resource,	simplebus_alloc_resource),
87 	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
88 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
89 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
90 	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
91 	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
92 	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
93 	DEVMETHOD(bus_child_pnpinfo,	ofw_bus_gen_child_pnpinfo),
94 	DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list),
95 	DEVMETHOD(bus_get_property,	simplebus_get_property),
96 	DEVMETHOD(bus_get_device_path,  ofw_bus_gen_get_device_path),
97 
98 	/* ofw_bus interface */
99 	DEVMETHOD(ofw_bus_get_devinfo,	simplebus_get_devinfo),
100 	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
101 	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
102 	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
103 	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
104 	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
105 
106 	DEVMETHOD_END
107 };
108 
109 DEFINE_CLASS_0(simplebus, simplebus_driver, simplebus_methods,
110     sizeof(struct simplebus_softc));
111 
112 EARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, 0, 0, BUS_PASS_BUS);
113 EARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, 0, 0,
114     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
115 
116 static int
117 simplebus_probe(device_t dev)
118 {
119 
120 	if (!ofw_bus_status_okay(dev))
121 		return (ENXIO);
122 	/*
123 	 * XXX We should attach only to pure' compatible = "simple-bus"',
124 	 * without any other compatible string.
125 	 * For now, filter only know cases:
126 	 * "syscon", "simple-bus"; is handled by fdt/syscon driver
127 	 * "simple-mfd", "simple-bus"; is handled by fdt/simple-mfd driver
128 	 */
129 	if (ofw_bus_is_compatible(dev, "syscon") ||
130 	    ofw_bus_is_compatible(dev, "simple-mfd"))
131 		return (ENXIO);
132 
133 	/*
134 	 * FDT data puts a "simple-bus" compatible string on many things that
135 	 * have children but aren't really buses in our world.  Without a
136 	 * ranges property we will fail to attach, so just fail to probe too.
137 	 */
138 	if (!(ofw_bus_is_compatible(dev, "simple-bus") &&
139 	    ofw_bus_has_prop(dev, "ranges")) &&
140 	    (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev),
141 	     "soc") != 0))
142 		return (ENXIO);
143 
144 	device_set_desc(dev, "Flattened device tree simple bus");
145 
146 	return (BUS_PROBE_GENERIC);
147 }
148 
149 int
150 simplebus_attach_impl(device_t dev)
151 {
152 	struct		simplebus_softc *sc;
153 	phandle_t	node;
154 
155 	sc = device_get_softc(dev);
156 	simplebus_init(dev, 0);
157 	if ((sc->flags & SB_FLAG_NO_RANGES) == 0 &&
158 	    simplebus_fill_ranges(sc->node, sc) < 0) {
159 		device_printf(dev, "could not get ranges\n");
160 		return (ENXIO);
161 	}
162 
163 	/*
164 	 * In principle, simplebus could have an interrupt map, but ignore that
165 	 * for now
166 	 */
167 
168 	for (node = OF_child(sc->node); node > 0; node = OF_peer(node))
169 		simplebus_add_device(dev, node, 0, NULL, -1, NULL);
170 
171 	return (0);
172 }
173 
174 int
175 simplebus_attach(device_t dev)
176 {
177 	int	rv;
178 
179 	rv = simplebus_attach_impl(dev);
180 	if (rv != 0)
181 		return (rv);
182 
183 	return (bus_generic_attach(dev));
184 }
185 
186 int
187 simplebus_detach(device_t dev)
188 {
189 	struct		simplebus_softc *sc;
190 
191 	sc = device_get_softc(dev);
192 	if (sc->ranges != NULL)
193 		free(sc->ranges, M_DEVBUF);
194 
195 	return (bus_generic_detach(dev));
196 }
197 
198 void
199 simplebus_init(device_t dev, phandle_t node)
200 {
201 	struct simplebus_softc *sc;
202 
203 	sc = device_get_softc(dev);
204 	if (node == 0)
205 		node = ofw_bus_get_node(dev);
206 	sc->dev = dev;
207 	sc->node = node;
208 
209 	/*
210 	 * Some important numbers
211 	 */
212 	sc->acells = 2;
213 	OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells));
214 	sc->scells = 1;
215 	OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells));
216 }
217 
218 int
219 simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc)
220 {
221 	int host_address_cells;
222 	cell_t *base_ranges;
223 	ssize_t nbase_ranges;
224 	int err;
225 	int i, j, k;
226 
227 	err = OF_searchencprop(OF_parent(node), "#address-cells",
228 	    &host_address_cells, sizeof(host_address_cells));
229 	if (err <= 0)
230 		return (-1);
231 
232 	nbase_ranges = OF_getproplen(node, "ranges");
233 	if (nbase_ranges < 0)
234 		return (-1);
235 	sc->nranges = nbase_ranges / sizeof(cell_t) /
236 	    (sc->acells + host_address_cells + sc->scells);
237 	if (sc->nranges == 0)
238 		return (0);
239 
240 	sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
241 	    M_DEVBUF, M_WAITOK);
242 	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
243 	OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
244 
245 	for (i = 0, j = 0; i < sc->nranges; i++) {
246 		sc->ranges[i].bus = 0;
247 		for (k = 0; k < sc->acells; k++) {
248 			sc->ranges[i].bus <<= 32;
249 			sc->ranges[i].bus |= base_ranges[j++];
250 		}
251 		sc->ranges[i].host = 0;
252 		for (k = 0; k < host_address_cells; k++) {
253 			sc->ranges[i].host <<= 32;
254 			sc->ranges[i].host |= base_ranges[j++];
255 		}
256 		sc->ranges[i].size = 0;
257 		for (k = 0; k < sc->scells; k++) {
258 			sc->ranges[i].size <<= 32;
259 			sc->ranges[i].size |= base_ranges[j++];
260 		}
261 	}
262 
263 	free(base_ranges, M_DEVBUF);
264 	return (sc->nranges);
265 }
266 
267 struct simplebus_devinfo *
268 simplebus_setup_dinfo(device_t dev, phandle_t node,
269     struct simplebus_devinfo *di)
270 {
271 	struct simplebus_softc *sc;
272 	struct simplebus_devinfo *ndi;
273 
274 	sc = device_get_softc(dev);
275 	if (di == NULL)
276 		ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
277 	else
278 		ndi = di;
279 	if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) {
280 		if (di == NULL)
281 			free(ndi, M_DEVBUF);
282 		return (NULL);
283 	}
284 
285 	resource_list_init(&ndi->rl);
286 	ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, &ndi->rl);
287 	ofw_bus_intr_to_rl(dev, node, &ndi->rl, NULL);
288 
289 	return (ndi);
290 }
291 
292 device_t
293 simplebus_add_device(device_t dev, phandle_t node, u_int order,
294     const char *name, int unit, struct simplebus_devinfo *di)
295 {
296 	struct simplebus_devinfo *ndi;
297 	device_t cdev;
298 
299 	if ((ndi = simplebus_setup_dinfo(dev, node, di)) == NULL)
300 		return (NULL);
301 	cdev = device_add_child_ordered(dev, order, name, unit);
302 	if (cdev == NULL) {
303 		device_printf(dev, "<%s>: device_add_child failed\n",
304 		    ndi->obdinfo.obd_name);
305 		resource_list_free(&ndi->rl);
306 		ofw_bus_gen_destroy_devinfo(&ndi->obdinfo);
307 		if (di == NULL)
308 			free(ndi, M_DEVBUF);
309 		return (NULL);
310 	}
311 	device_set_ivars(cdev, ndi);
312 
313 	return(cdev);
314 }
315 
316 static device_t
317 simplebus_add_child(device_t dev, u_int order, const char *name, int unit)
318 {
319 	device_t cdev;
320 	struct simplebus_devinfo *ndi;
321 
322 	cdev = device_add_child_ordered(dev, order, name, unit);
323 	if (cdev == NULL)
324 		return (NULL);
325 
326 	ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
327 	ndi->obdinfo.obd_node = -1;
328 	resource_list_init(&ndi->rl);
329 	device_set_ivars(cdev, ndi);
330 
331 	return (cdev);
332 }
333 
334 static const struct ofw_bus_devinfo *
335 simplebus_get_devinfo(device_t bus __unused, device_t child)
336 {
337         struct simplebus_devinfo *ndi;
338 
339         ndi = device_get_ivars(child);
340 	if (ndi == NULL)
341 		return (NULL);
342         return (&ndi->obdinfo);
343 }
344 
345 static struct resource_list *
346 simplebus_get_resource_list(device_t bus __unused, device_t child)
347 {
348 	struct simplebus_devinfo *ndi;
349 
350 	ndi = device_get_ivars(child);
351 	if (ndi == NULL)
352 		return (NULL);
353 	return (&ndi->rl);
354 }
355 
356 static ssize_t
357 simplebus_get_property(device_t bus, device_t child, const char *propname,
358     void *propvalue, size_t size, device_property_type_t type)
359 {
360 	phandle_t node, xref;
361 	ssize_t ret, i;
362 	uint32_t *buffer;
363 	uint64_t val;
364 
365 	switch (type) {
366 	case DEVICE_PROP_ANY:
367 	case DEVICE_PROP_BUFFER:
368 	case DEVICE_PROP_UINT32:
369 	case DEVICE_PROP_UINT64:
370 	case DEVICE_PROP_HANDLE:
371 		break;
372 	default:
373 		return (-1);
374 	}
375 
376 	node = ofw_bus_get_node(child);
377 	if (propvalue == NULL || size == 0)
378 		return (OF_getproplen(node, propname));
379 
380 	/*
381 	 * Integer values are stored in BE format.
382 	 * If caller declared that the underlying property type is uint32_t
383 	 * we need to do the conversion to match host endianness.
384 	 */
385 	if (type == DEVICE_PROP_UINT32)
386 		return (OF_getencprop(node, propname, propvalue, size));
387 
388 	/*
389 	 * uint64_t also requires endianness handling.
390 	 * In FDT every 8 byte value is stored using two uint32_t variables
391 	 * in BE format. Now, since the upper bits are stored as the first
392 	 * of the pair, both halves require swapping.
393 	 */
394 	 if (type == DEVICE_PROP_UINT64) {
395 		ret = OF_getencprop(node, propname, propvalue, size);
396 		if (ret <= 0) {
397 			return (ret);
398 		}
399 
400 		buffer = (uint32_t *)propvalue;
401 
402 		for (i = 0; i < size / 4; i += 2) {
403 			val = (uint64_t)buffer[i] << 32 | buffer[i + 1];
404 			((uint64_t *)buffer)[i / 2] = val;
405 		}
406 		return (ret);
407 	}
408 
409 	if (type == DEVICE_PROP_HANDLE) {
410 		if (size < sizeof(node))
411 			return (-1);
412 		ret = OF_getencprop(node, propname, &xref, sizeof(xref));
413 		if (ret <= 0)
414 			return (ret);
415 
416 		node = OF_node_from_xref(xref);
417 		if (propvalue != NULL)
418 			*(uint32_t *)propvalue = node;
419 		return (ret);
420 	}
421 
422 	return (OF_getprop(node, propname, propvalue, size));
423 }
424 
425 static struct resource *
426 simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
427     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
428 {
429 	struct simplebus_softc *sc;
430 	struct simplebus_devinfo *di;
431 	struct resource_list_entry *rle;
432 	int j;
433 
434 	sc = device_get_softc(bus);
435 
436 	/*
437 	 * Request for the default allocation with a given rid: use resource
438 	 * list stored in the local device info.
439 	 */
440 	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
441 		if ((di = device_get_ivars(child)) == NULL)
442 			return (NULL);
443 
444 		if (type == SYS_RES_IOPORT)
445 			type = SYS_RES_MEMORY;
446 
447 		rle = resource_list_find(&di->rl, type, *rid);
448 		if (rle == NULL) {
449 			if (bootverbose)
450 				device_printf(bus, "no default resources for "
451 				    "rid = %d, type = %d\n", *rid, type);
452 			return (NULL);
453 		}
454 		start = rle->start;
455 		end = rle->end;
456 		count = rle->count;
457         }
458 
459 	if (type == SYS_RES_MEMORY) {
460 		/* Remap through ranges property */
461 		for (j = 0; j < sc->nranges; j++) {
462 			if (start >= sc->ranges[j].bus && end <
463 			    sc->ranges[j].bus + sc->ranges[j].size) {
464 				start -= sc->ranges[j].bus;
465 				start += sc->ranges[j].host;
466 				end -= sc->ranges[j].bus;
467 				end += sc->ranges[j].host;
468 				break;
469 			}
470 		}
471 		if (j == sc->nranges && sc->nranges != 0) {
472 			if (bootverbose)
473 				device_printf(bus, "Could not map resource "
474 				    "%#jx-%#jx\n", start, end);
475 
476 			return (NULL);
477 		}
478 	}
479 
480 	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
481 	    count, flags));
482 }
483 
484 static int
485 simplebus_print_res(struct simplebus_devinfo *di)
486 {
487 	int rv;
488 
489 	if (di == NULL)
490 		return (0);
491 	rv = 0;
492 	rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#jx");
493 	rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%jd");
494 	return (rv);
495 }
496 
497 static void
498 simplebus_probe_nomatch(device_t bus, device_t child)
499 {
500 	const char *name, *type, *compat;
501 
502 	if (!bootverbose)
503 		return;
504 
505 	compat = ofw_bus_get_compat(child);
506 	if (compat == NULL)
507 		return;
508 	name = ofw_bus_get_name(child);
509 	type = ofw_bus_get_type(child);
510 
511 	device_printf(bus, "<%s>", name != NULL ? name : "unknown");
512 	simplebus_print_res(device_get_ivars(child));
513 	if (!ofw_bus_status_okay(child))
514 		printf(" disabled");
515 	if (type)
516 		printf(" type %s", type);
517 	printf(" compat %s (no driver attached)\n", compat);
518 }
519 
520 static int
521 simplebus_print_child(device_t bus, device_t child)
522 {
523 	int rv;
524 
525 	rv = bus_print_child_header(bus, child);
526 	rv += simplebus_print_res(device_get_ivars(child));
527 	if (!ofw_bus_status_okay(child))
528 		rv += printf(" disabled");
529 	rv += bus_print_child_footer(bus, child);
530 	return (rv);
531 }
532