xref: /freebsd/sys/dev/fdt/simplebus.c (revision c9ccf3a32da427475985b85d7df023ccfb138c27)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 #include <sys/conf.h>
36 #include <sys/kernel.h>
37 #include <sys/rman.h>
38 
39 #include <dev/ofw/openfirm.h>
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/ofw_bus_subr.h>
42 
43 #include <dev/fdt/simplebus.h>
44 
45 /*
46  * Bus interface.
47  */
48 static int		simplebus_probe(device_t dev);
49 static struct resource *simplebus_alloc_resource(device_t, device_t, int,
50     int *, rman_res_t, rman_res_t, rman_res_t, u_int);
51 static void		simplebus_probe_nomatch(device_t bus, device_t child);
52 static int		simplebus_print_child(device_t bus, device_t child);
53 static device_t		simplebus_add_child(device_t dev, u_int order,
54     const char *name, int unit);
55 static struct resource_list *simplebus_get_resource_list(device_t bus,
56     device_t child);
57 
58 static ssize_t		simplebus_get_property(device_t bus, device_t child,
59     const char *propname, void *propvalue, size_t size,
60     device_property_type_t type);
61 /*
62  * ofw_bus interface
63  */
64 static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus,
65     device_t child);
66 
67 /*
68  * Driver methods.
69  */
70 static device_method_t	simplebus_methods[] = {
71 	/* Device interface */
72 	DEVMETHOD(device_probe,		simplebus_probe),
73 	DEVMETHOD(device_attach,	simplebus_attach),
74 	DEVMETHOD(device_detach,	simplebus_detach),
75 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
76 	DEVMETHOD(device_suspend,	bus_generic_suspend),
77 	DEVMETHOD(device_resume,	bus_generic_resume),
78 
79 	/* Bus interface */
80 	DEVMETHOD(bus_add_child,	simplebus_add_child),
81 	DEVMETHOD(bus_print_child,	simplebus_print_child),
82 	DEVMETHOD(bus_probe_nomatch,	simplebus_probe_nomatch),
83 	DEVMETHOD(bus_read_ivar,	bus_generic_read_ivar),
84 	DEVMETHOD(bus_write_ivar,	bus_generic_write_ivar),
85 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
86 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
87 	DEVMETHOD(bus_alloc_resource,	simplebus_alloc_resource),
88 	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
89 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
90 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
91 	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
92 	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
93 	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
94 	DEVMETHOD(bus_child_pnpinfo,	ofw_bus_gen_child_pnpinfo),
95 	DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list),
96 	DEVMETHOD(bus_get_property,	simplebus_get_property),
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 = ofw_bus_get_node(child);
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 		break;
371 	default:
372 		return (-1);
373 	}
374 
375 	if (propvalue == NULL || size == 0)
376 		return (OF_getproplen(node, propname));
377 
378 	/*
379 	 * Integer values are stored in BE format.
380 	 * If caller declared that the underlying property type is uint32_t
381 	 * we need to do the conversion to match host endianness.
382 	 */
383 	if (type == DEVICE_PROP_UINT32)
384 		return (OF_getencprop(node, propname, propvalue, size));
385 
386 	/*
387 	 * uint64_t also requires endianness handling.
388 	 * In FDT every 8 byte value is stored using two uint32_t variables
389 	 * in BE format. Now, since the upper bits are stored as the first
390 	 * of the pair, both halves require swapping.
391 	 */
392 	 if (type == DEVICE_PROP_UINT64) {
393 		ret = OF_getencprop(node, propname, propvalue, size);
394 		if (ret <= 0) {
395 			return (ret);
396 		}
397 
398 		buffer = (uint32_t *)propvalue;
399 
400 		for (i = 0; i < size / 4; i += 2) {
401 			val = (uint64_t)buffer[i] << 32 | buffer[i + 1];
402 			((uint64_t *)buffer)[i / 2] = val;
403 		}
404 		return (ret);
405 	 }
406 
407 	return (OF_getprop(node, propname, propvalue, size));
408 }
409 
410 static struct resource *
411 simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
412     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
413 {
414 	struct simplebus_softc *sc;
415 	struct simplebus_devinfo *di;
416 	struct resource_list_entry *rle;
417 	int j;
418 
419 	sc = device_get_softc(bus);
420 
421 	/*
422 	 * Request for the default allocation with a given rid: use resource
423 	 * list stored in the local device info.
424 	 */
425 	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
426 		if ((di = device_get_ivars(child)) == NULL)
427 			return (NULL);
428 
429 		if (type == SYS_RES_IOPORT)
430 			type = SYS_RES_MEMORY;
431 
432 		rle = resource_list_find(&di->rl, type, *rid);
433 		if (rle == NULL) {
434 			if (bootverbose)
435 				device_printf(bus, "no default resources for "
436 				    "rid = %d, type = %d\n", *rid, type);
437 			return (NULL);
438 		}
439 		start = rle->start;
440 		end = rle->end;
441 		count = rle->count;
442         }
443 
444 	if (type == SYS_RES_MEMORY) {
445 		/* Remap through ranges property */
446 		for (j = 0; j < sc->nranges; j++) {
447 			if (start >= sc->ranges[j].bus && end <
448 			    sc->ranges[j].bus + sc->ranges[j].size) {
449 				start -= sc->ranges[j].bus;
450 				start += sc->ranges[j].host;
451 				end -= sc->ranges[j].bus;
452 				end += sc->ranges[j].host;
453 				break;
454 			}
455 		}
456 		if (j == sc->nranges && sc->nranges != 0) {
457 			if (bootverbose)
458 				device_printf(bus, "Could not map resource "
459 				    "%#jx-%#jx\n", start, end);
460 
461 			return (NULL);
462 		}
463 	}
464 
465 	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
466 	    count, flags));
467 }
468 
469 static int
470 simplebus_print_res(struct simplebus_devinfo *di)
471 {
472 	int rv;
473 
474 	if (di == NULL)
475 		return (0);
476 	rv = 0;
477 	rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#jx");
478 	rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%jd");
479 	return (rv);
480 }
481 
482 static void
483 simplebus_probe_nomatch(device_t bus, device_t child)
484 {
485 	const char *name, *type, *compat;
486 
487 	if (!bootverbose)
488 		return;
489 
490 	compat = ofw_bus_get_compat(child);
491 	if (compat == NULL)
492 		return;
493 	name = ofw_bus_get_name(child);
494 	type = ofw_bus_get_type(child);
495 
496 	device_printf(bus, "<%s>", name != NULL ? name : "unknown");
497 	simplebus_print_res(device_get_ivars(child));
498 	if (!ofw_bus_status_okay(child))
499 		printf(" disabled");
500 	if (type)
501 		printf(" type %s", type);
502 	printf(" compat %s (no driver attached)\n", compat);
503 }
504 
505 static int
506 simplebus_print_child(device_t bus, device_t child)
507 {
508 	int rv;
509 
510 	rv = bus_print_child_header(bus, child);
511 	rv += simplebus_print_res(device_get_ivars(child));
512 	if (!ofw_bus_status_okay(child))
513 		rv += printf(" disabled");
514 	rv += bus_print_child_footer(bus, child);
515 	return (rv);
516 }
517