1208747Sraj/*-
2266128Sian * Copyright (c) 2013 Nathan Whitehorn
3208747Sraj * All rights reserved.
4208747Sraj *
5208747Sraj * Redistribution and use in source and binary forms, with or without
6208747Sraj * modification, are permitted provided that the following conditions
7208747Sraj * are met:
8208747Sraj * 1. Redistributions of source code must retain the above copyright
9208747Sraj *    notice, this list of conditions and the following disclaimer.
10208747Sraj * 2. Redistributions in binary form must reproduce the above copyright
11208747Sraj *    notice, this list of conditions and the following disclaimer in the
12208747Sraj *    documentation and/or other materials provided with the distribution.
13208747Sraj *
14208747Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15208747Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16208747Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17266128Sian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18208747Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19208747Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20208747Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21208747Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22208747Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23208747Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24208747Sraj * SUCH DAMAGE.
25208747Sraj */
26208747Sraj
27208747Sraj#include <sys/cdefs.h>
28208747Sraj__FBSDID("$FreeBSD: releng/10.2/sys/dev/fdt/simplebus.c 283477 2015-05-24 17:51:57Z ian $");
29208747Sraj#include <sys/param.h>
30208747Sraj#include <sys/systm.h>
31208747Sraj#include <sys/module.h>
32208747Sraj#include <sys/bus.h>
33266128Sian#include <sys/conf.h>
34266128Sian#include <sys/kernel.h>
35208747Sraj#include <sys/rman.h>
36208747Sraj
37266128Sian#include <dev/ofw/openfirm.h>
38208747Sraj#include <dev/ofw/ofw_bus.h>
39208747Sraj#include <dev/ofw/ofw_bus_subr.h>
40208747Sraj
41283477Sian#include <dev/fdt/simplebus.h>
42208747Sraj
43208747Sraj/*
44266128Sian * Bus interface.
45208747Sraj */
46266128Sianstatic int		simplebus_probe(device_t dev);
47266128Sianstatic int		simplebus_attach(device_t dev);
48208747Srajstatic struct resource *simplebus_alloc_resource(device_t, device_t, int,
49208747Sraj    int *, u_long, u_long, u_long, u_int);
50266128Sianstatic void		simplebus_probe_nomatch(device_t bus, device_t child);
51266128Sianstatic int		simplebus_print_child(device_t bus, device_t child);
52283477Sianstatic device_t		simplebus_add_child(device_t dev, u_int order,
53283477Sian    const char *name, int unit);
54283477Sianstatic struct resource_list *simplebus_get_resource_list(device_t bus,
55283477Sian    device_t child);
56266128Sian/*
57266128Sian * ofw_bus interface
58266128Sian */
59266128Sianstatic const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus,
60266128Sian    device_t child);
61208747Sraj
62208747Sraj/*
63266128Sian * local methods
64208747Sraj */
65266128Sian
66266128Sianstatic int simplebus_fill_ranges(phandle_t node,
67266128Sian    struct simplebus_softc *sc);
68266128Sian
69266128Sian/*
70266128Sian * Driver methods.
71266128Sian */
72266128Sianstatic device_method_t	simplebus_methods[] = {
73208747Sraj	/* Device interface */
74208747Sraj	DEVMETHOD(device_probe,		simplebus_probe),
75208747Sraj	DEVMETHOD(device_attach,	simplebus_attach),
76283477Sian	DEVMETHOD(device_detach,	bus_generic_detach),
77283477Sian	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
78283477Sian	DEVMETHOD(device_suspend,	bus_generic_suspend),
79283477Sian	DEVMETHOD(device_resume,	bus_generic_resume),
80208747Sraj
81208747Sraj	/* Bus interface */
82283477Sian	DEVMETHOD(bus_add_child,	simplebus_add_child),
83208747Sraj	DEVMETHOD(bus_print_child,	simplebus_print_child),
84266128Sian	DEVMETHOD(bus_probe_nomatch,	simplebus_probe_nomatch),
85283477Sian	DEVMETHOD(bus_read_ivar,	bus_generic_read_ivar),
86283477Sian	DEVMETHOD(bus_write_ivar,	bus_generic_write_ivar),
87266128Sian	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
88266128Sian	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
89208747Sraj	DEVMETHOD(bus_alloc_resource,	simplebus_alloc_resource),
90266128Sian	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
91266128Sian	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
92266128Sian	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
93266128Sian	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
94283477Sian	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
95283477Sian	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
96266128Sian	DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
97283477Sian	DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list),
98208747Sraj
99266128Sian	/* ofw_bus interface */
100208747Sraj	DEVMETHOD(ofw_bus_get_devinfo,	simplebus_get_devinfo),
101208747Sraj	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
102208747Sraj	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
103208747Sraj	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
104208747Sraj	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
105208747Sraj	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
106208747Sraj
107266128Sian	DEVMETHOD_END
108208747Sraj};
109208747Sraj
110283477SianDEFINE_CLASS_0(simplebus, simplebus_driver, simplebus_methods,
111283477Sian    sizeof(struct simplebus_softc));
112283477Sian
113266128Sianstatic devclass_t simplebus_devclass;
114270075SianEARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass,
115270075Sian    0, 0, BUS_PASS_BUS);
116270075SianEARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass,
117270075Sian    0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
118208747Sraj
119208747Srajstatic int
120208747Srajsimplebus_probe(device_t dev)
121208747Sraj{
122266128Sian
123266152Sian	if (!ofw_bus_status_okay(dev))
124266152Sian		return (ENXIO);
125266152Sian
126273678Sian	/*
127273678Sian	 * FDT data puts a "simple-bus" compatible string on many things that
128273678Sian	 * have children but aren't really busses in our world.  Without a
129273678Sian	 * ranges property we will fail to attach, so just fail to probe too.
130273678Sian	 */
131273678Sian	if (!(ofw_bus_is_compatible(dev, "simple-bus") &&
132273678Sian	    ofw_bus_has_prop(dev, "ranges")) &&
133266128Sian	    (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev),
134266128Sian	     "soc") != 0))
135208747Sraj		return (ENXIO);
136208747Sraj
137208747Sraj	device_set_desc(dev, "Flattened device tree simple bus");
138208747Sraj
139248480Sray	return (BUS_PROBE_GENERIC);
140208747Sraj}
141208747Sraj
142208747Srajstatic int
143208747Srajsimplebus_attach(device_t dev)
144208747Sraj{
145266128Sian	struct		simplebus_softc *sc;
146266128Sian	phandle_t	node;
147208747Sraj
148208747Sraj	sc = device_get_softc(dev);
149283477Sian	simplebus_init(dev, 0);
150283477Sian	if (simplebus_fill_ranges(sc->node, sc) < 0) {
151283477Sian		device_printf(dev, "could not get ranges\n");
152283477Sian		return (ENXIO);
153283477Sian	}
154208747Sraj
155283477Sian	/*
156283477Sian	 * In principle, simplebus could have an interrupt map, but ignore that
157283477Sian	 * for now
158283477Sian	 */
159283477Sian
160283477Sian	for (node = OF_child(sc->node); node > 0; node = OF_peer(node))
161283477Sian		simplebus_add_device(dev, node, 0, NULL, -1, NULL);
162283477Sian	return (bus_generic_attach(dev));
163283477Sian}
164283477Sian
165283477Sianvoid
166283477Siansimplebus_init(device_t dev, phandle_t node)
167283477Sian{
168283477Sian	struct simplebus_softc *sc;
169283477Sian
170283477Sian	sc = device_get_softc(dev);
171283477Sian	if (node == 0)
172283477Sian		node = ofw_bus_get_node(dev);
173266128Sian	sc->dev = dev;
174266128Sian	sc->node = node;
175266128Sian
176208747Sraj	/*
177266128Sian	 * Some important numbers
178208747Sraj	 */
179266128Sian	sc->acells = 2;
180266128Sian	OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells));
181266128Sian	sc->scells = 1;
182266128Sian	OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells));
183266128Sian}
184208747Sraj
185266128Sianstatic int
186266128Siansimplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc)
187266128Sian{
188266128Sian	int host_address_cells;
189266128Sian	cell_t *base_ranges;
190266128Sian	ssize_t nbase_ranges;
191266128Sian	int err;
192266128Sian	int i, j, k;
193266128Sian
194266128Sian	err = OF_searchencprop(OF_parent(node), "#address-cells",
195266128Sian	    &host_address_cells, sizeof(host_address_cells));
196266128Sian	if (err <= 0)
197266128Sian		return (-1);
198266128Sian
199266128Sian	nbase_ranges = OF_getproplen(node, "ranges");
200266128Sian	if (nbase_ranges < 0)
201266128Sian		return (-1);
202266128Sian	sc->nranges = nbase_ranges / sizeof(cell_t) /
203266128Sian	    (sc->acells + host_address_cells + sc->scells);
204266128Sian	if (sc->nranges == 0)
205266128Sian		return (0);
206266128Sian
207266128Sian	sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
208266128Sian	    M_DEVBUF, M_WAITOK);
209266128Sian	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
210266128Sian	OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
211266128Sian
212266128Sian	for (i = 0, j = 0; i < sc->nranges; i++) {
213266128Sian		sc->ranges[i].bus = 0;
214266128Sian		for (k = 0; k < sc->acells; k++) {
215266128Sian			sc->ranges[i].bus <<= 32;
216266128Sian			sc->ranges[i].bus |= base_ranges[j++];
217208747Sraj		}
218266128Sian		sc->ranges[i].host = 0;
219266128Sian		for (k = 0; k < host_address_cells; k++) {
220266128Sian			sc->ranges[i].host <<= 32;
221266128Sian			sc->ranges[i].host |= base_ranges[j++];
222266128Sian		}
223266128Sian		sc->ranges[i].size = 0;
224266128Sian		for (k = 0; k < sc->scells; k++) {
225266128Sian			sc->ranges[i].size <<= 32;
226266128Sian			sc->ranges[i].size |= base_ranges[j++];
227266128Sian		}
228208747Sraj	}
229208747Sraj
230266128Sian	free(base_ranges, M_DEVBUF);
231266128Sian	return (sc->nranges);
232208747Sraj}
233208747Sraj
234283477Sianstruct simplebus_devinfo *
235283477Siansimplebus_setup_dinfo(device_t dev, phandle_t node,
236283477Sian    struct simplebus_devinfo *di)
237208747Sraj{
238266128Sian	struct simplebus_softc *sc;
239266128Sian	struct simplebus_devinfo *ndi;
240208747Sraj
241266128Sian	sc = device_get_softc(dev);
242283477Sian	if (di == NULL)
243283477Sian		ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
244283477Sian	else
245283477Sian		ndi = di;
246266128Sian	if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) {
247283477Sian		if (di == NULL)
248283477Sian			free(ndi, M_DEVBUF);
249266128Sian		return (NULL);
250266128Sian	}
251208747Sraj
252266128Sian	resource_list_init(&ndi->rl);
253283334Sian	ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, &ndi->rl);
254273675Sian	ofw_bus_intr_to_rl(dev, node, &ndi->rl);
255266128Sian
256266128Sian	return (ndi);
257208747Sraj}
258208747Sraj
259283477Siandevice_t
260283477Siansimplebus_add_device(device_t dev, phandle_t node, u_int order,
261283477Sian    const char *name, int unit, struct simplebus_devinfo *di)
262283477Sian{
263283477Sian	struct simplebus_devinfo *ndi;
264283477Sian	device_t cdev;
265283477Sian
266283477Sian	if ((ndi = simplebus_setup_dinfo(dev, node, di)) == NULL)
267283477Sian		return (NULL);
268283477Sian	cdev = device_add_child_ordered(dev, order, name, unit);
269283477Sian	if (cdev == NULL) {
270283477Sian		device_printf(dev, "<%s>: device_add_child failed\n",
271283477Sian		    ndi->obdinfo.obd_name);
272283477Sian		resource_list_free(&ndi->rl);
273283477Sian		ofw_bus_gen_destroy_devinfo(&ndi->obdinfo);
274283477Sian		if (di == NULL)
275283477Sian			free(ndi, M_DEVBUF);
276283477Sian		return (NULL);
277283477Sian	}
278283477Sian	device_set_ivars(cdev, ndi);
279283477Sian
280283477Sian	return(cdev);
281283477Sian}
282283477Sian
283283477Sianstatic device_t
284283477Siansimplebus_add_child(device_t dev, u_int order, const char *name, int unit)
285283477Sian{
286283477Sian	device_t cdev;
287283477Sian	struct simplebus_devinfo *ndi;
288283477Sian
289283477Sian	cdev = device_add_child_ordered(dev, order, name, unit);
290283477Sian	if (cdev == NULL)
291283477Sian		return (NULL);
292283477Sian
293283477Sian	ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
294283477Sian	ndi->obdinfo.obd_node = -1;
295283477Sian	resource_list_init(&ndi->rl);
296283477Sian	device_set_ivars(cdev, ndi);
297283477Sian
298283477Sian	return (cdev);
299283477Sian}
300283477Sian
301266128Sianstatic const struct ofw_bus_devinfo *
302266128Siansimplebus_get_devinfo(device_t bus __unused, device_t child)
303266128Sian{
304266128Sian        struct simplebus_devinfo *ndi;
305266128Sian
306266128Sian        ndi = device_get_ivars(child);
307266128Sian        return (&ndi->obdinfo);
308266128Sian}
309266128Sian
310283477Sianstatic struct resource_list *
311283477Siansimplebus_get_resource_list(device_t bus __unused, device_t child)
312283477Sian{
313283477Sian	struct simplebus_devinfo *ndi;
314283477Sian
315283477Sian	ndi = device_get_ivars(child);
316283477Sian	return (&ndi->rl);
317283477Sian}
318283477Sian
319208747Srajstatic struct resource *
320208747Srajsimplebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
321208747Sraj    u_long start, u_long end, u_long count, u_int flags)
322208747Sraj{
323266128Sian	struct simplebus_softc *sc;
324208747Sraj	struct simplebus_devinfo *di;
325208747Sraj	struct resource_list_entry *rle;
326266128Sian	int j;
327208747Sraj
328266128Sian	sc = device_get_softc(bus);
329266128Sian
330208747Sraj	/*
331208747Sraj	 * Request for the default allocation with a given rid: use resource
332208747Sraj	 * list stored in the local device info.
333208747Sraj	 */
334208747Sraj	if ((start == 0UL) && (end == ~0UL)) {
335208747Sraj		if ((di = device_get_ivars(child)) == NULL)
336208747Sraj			return (NULL);
337208747Sraj
338208747Sraj		if (type == SYS_RES_IOPORT)
339208747Sraj			type = SYS_RES_MEMORY;
340208747Sraj
341266128Sian		rle = resource_list_find(&di->rl, type, *rid);
342208747Sraj		if (rle == NULL) {
343248481Sray			if (bootverbose)
344248481Sray				device_printf(bus, "no default resources for "
345248481Sray				    "rid = %d, type = %d\n", *rid, type);
346208747Sraj			return (NULL);
347208747Sraj		}
348208747Sraj		start = rle->start;
349208747Sraj		end = rle->end;
350208747Sraj		count = rle->count;
351266128Sian        }
352266128Sian
353266128Sian	if (type == SYS_RES_MEMORY) {
354266128Sian		/* Remap through ranges property */
355266128Sian		for (j = 0; j < sc->nranges; j++) {
356266128Sian			if (start >= sc->ranges[j].bus && end <
357266128Sian			    sc->ranges[j].bus + sc->ranges[j].size) {
358266128Sian				start -= sc->ranges[j].bus;
359266128Sian				start += sc->ranges[j].host;
360266128Sian				end -= sc->ranges[j].bus;
361266128Sian				end += sc->ranges[j].host;
362266128Sian				break;
363266128Sian			}
364266128Sian		}
365266128Sian		if (j == sc->nranges && sc->nranges != 0) {
366266128Sian			if (bootverbose)
367266128Sian				device_printf(bus, "Could not map resource "
368266128Sian				    "%#lx-%#lx\n", start, end);
369266128Sian
370266128Sian			return (NULL);
371266128Sian		}
372208747Sraj	}
373208747Sraj
374208747Sraj	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
375208747Sraj	    count, flags));
376208747Sraj}
377208747Sraj
378257457Sbrooksstatic int
379266128Siansimplebus_print_res(struct simplebus_devinfo *di)
380257457Sbrooks{
381266128Sian	int rv;
382257457Sbrooks
383266128Sian	rv = 0;
384266128Sian	rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx");
385266128Sian	rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld");
386266128Sian	return (rv);
387257457Sbrooks}
388257457Sbrooks
389266128Sianstatic void
390266128Siansimplebus_probe_nomatch(device_t bus, device_t child)
391257457Sbrooks{
392266196Sian	const char *name, *type, *compat;
393257457Sbrooks
394266128Sian	if (!bootverbose)
395266128Sian		return;
396257457Sbrooks
397283477Sian	compat = ofw_bus_get_compat(child);
398283477Sian	if (compat == NULL)
399283477Sian		return;
400266128Sian	name = ofw_bus_get_name(child);
401266128Sian	type = ofw_bus_get_type(child);
402257457Sbrooks
403266128Sian	device_printf(bus, "<%s>", name != NULL ? name : "unknown");
404266128Sian	simplebus_print_res(device_get_ivars(child));
405266196Sian	if (!ofw_bus_status_okay(child))
406266196Sian		printf(" disabled");
407266196Sian	if (type)
408266196Sian		printf(" type %s", type);
409283477Sian	printf(" compat %s (no driver attached)\n", compat);
410257457Sbrooks}
411257457Sbrooks
412208747Srajstatic int
413266128Siansimplebus_print_child(device_t bus, device_t child)
414208747Sraj{
415266128Sian	int rv;
416208747Sraj
417266128Sian	rv = bus_print_child_header(bus, child);
418266128Sian	rv += simplebus_print_res(device_get_ivars(child));
419266196Sian	if (!ofw_bus_status_okay(child))
420266196Sian		rv += printf(" disabled");
421266128Sian	rv += bus_print_child_footer(bus, child);
422266128Sian	return (rv);
423208747Sraj}
424