1208747Sraj/*-
2261352Snwhitehorn * 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
17261352Snwhitehorn * 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/11.0/sys/dev/fdt/simplebus.c 301453 2016-06-05 16:20:12Z skra $");
29208747Sraj#include <sys/param.h>
30208747Sraj#include <sys/systm.h>
31208747Sraj#include <sys/module.h>
32208747Sraj#include <sys/bus.h>
33261352Snwhitehorn#include <sys/conf.h>
34261352Snwhitehorn#include <sys/kernel.h>
35208747Sraj#include <sys/rman.h>
36208747Sraj
37261352Snwhitehorn#include <dev/ofw/openfirm.h>
38208747Sraj#include <dev/ofw/ofw_bus.h>
39208747Sraj#include <dev/ofw/ofw_bus_subr.h>
40208747Sraj
41279368Sjchandra#include <dev/fdt/simplebus.h>
42208747Sraj
43208747Sraj/*
44261352Snwhitehorn * Bus interface.
45208747Sraj */
46261352Snwhitehornstatic int		simplebus_probe(device_t dev);
47261352Snwhitehornstatic int		simplebus_attach(device_t dev);
48208747Srajstatic struct resource *simplebus_alloc_resource(device_t, device_t, int,
49294883Sjhibbits    int *, rman_res_t, rman_res_t, rman_res_t, u_int);
50261352Snwhitehornstatic void		simplebus_probe_nomatch(device_t bus, device_t child);
51261352Snwhitehornstatic int		simplebus_print_child(device_t bus, device_t child);
52280772Sianstatic device_t		simplebus_add_child(device_t dev, u_int order,
53280772Sian    const char *name, int unit);
54280772Sianstatic struct resource_list *simplebus_get_resource_list(device_t bus,
55280772Sian    device_t child);
56261352Snwhitehorn/*
57261352Snwhitehorn * ofw_bus interface
58261352Snwhitehorn */
59261352Snwhitehornstatic const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus,
60261352Snwhitehorn    device_t child);
61208747Sraj
62208747Sraj/*
63261352Snwhitehorn * local methods
64208747Sraj */
65261352Snwhitehorn
66261352Snwhitehornstatic int simplebus_fill_ranges(phandle_t node,
67261352Snwhitehorn    struct simplebus_softc *sc);
68261352Snwhitehorn
69261352Snwhitehorn/*
70261352Snwhitehorn * Driver methods.
71261352Snwhitehorn */
72261352Snwhitehornstatic device_method_t	simplebus_methods[] = {
73208747Sraj	/* Device interface */
74208747Sraj	DEVMETHOD(device_probe,		simplebus_probe),
75208747Sraj	DEVMETHOD(device_attach,	simplebus_attach),
76280772Sian	DEVMETHOD(device_detach,	bus_generic_detach),
77280772Sian	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
78280772Sian	DEVMETHOD(device_suspend,	bus_generic_suspend),
79280772Sian	DEVMETHOD(device_resume,	bus_generic_resume),
80208747Sraj
81208747Sraj	/* Bus interface */
82280772Sian	DEVMETHOD(bus_add_child,	simplebus_add_child),
83208747Sraj	DEVMETHOD(bus_print_child,	simplebus_print_child),
84261352Snwhitehorn	DEVMETHOD(bus_probe_nomatch,	simplebus_probe_nomatch),
85280772Sian	DEVMETHOD(bus_read_ivar,	bus_generic_read_ivar),
86280772Sian	DEVMETHOD(bus_write_ivar,	bus_generic_write_ivar),
87261352Snwhitehorn	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
88261352Snwhitehorn	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
89208747Sraj	DEVMETHOD(bus_alloc_resource,	simplebus_alloc_resource),
90261352Snwhitehorn	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
91261352Snwhitehorn	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
92261352Snwhitehorn	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
93261352Snwhitehorn	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
94280772Sian	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
95280772Sian	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
96261352Snwhitehorn	DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
97280772Sian	DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list),
98208747Sraj
99261352Snwhitehorn	/* 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
107261352Snwhitehorn	DEVMETHOD_END
108208747Sraj};
109208747Sraj
110279368SjchandraDEFINE_CLASS_0(simplebus, simplebus_driver, simplebus_methods,
111279368Sjchandra    sizeof(struct simplebus_softc));
112279368Sjchandra
113261352Snwhitehornstatic devclass_t simplebus_devclass;
114269594SianEARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass,
115269594Sian    0, 0, BUS_PASS_BUS);
116269594SianEARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass,
117269597Sian    0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
118208747Sraj
119208747Srajstatic int
120208747Srajsimplebus_probe(device_t dev)
121208747Sraj{
122261352Snwhitehorn
123261410Sian	if (!ofw_bus_status_okay(dev))
124261410Sian		return (ENXIO);
125261410Sian
126273282Sian	/*
127273282Sian	 * FDT data puts a "simple-bus" compatible string on many things that
128273282Sian	 * have children but aren't really busses in our world.  Without a
129273282Sian	 * ranges property we will fail to attach, so just fail to probe too.
130273282Sian	 */
131273282Sian	if (!(ofw_bus_is_compatible(dev, "simple-bus") &&
132273282Sian	    ofw_bus_has_prop(dev, "ranges")) &&
133261352Snwhitehorn	    (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev),
134261352Snwhitehorn	     "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{
145261352Snwhitehorn	struct		simplebus_softc *sc;
146261352Snwhitehorn	phandle_t	node;
147208747Sraj
148208747Sraj	sc = device_get_softc(dev);
149280772Sian	simplebus_init(dev, 0);
150280772Sian	if (simplebus_fill_ranges(sc->node, sc) < 0) {
151280772Sian		device_printf(dev, "could not get ranges\n");
152280772Sian		return (ENXIO);
153280772Sian	}
154208747Sraj
155280772Sian	/*
156280772Sian	 * In principle, simplebus could have an interrupt map, but ignore that
157280772Sian	 * for now
158280772Sian	 */
159280772Sian
160280772Sian	for (node = OF_child(sc->node); node > 0; node = OF_peer(node))
161280772Sian		simplebus_add_device(dev, node, 0, NULL, -1, NULL);
162280772Sian	return (bus_generic_attach(dev));
163280772Sian}
164280772Sian
165280772Sianvoid
166280772Siansimplebus_init(device_t dev, phandle_t node)
167280772Sian{
168280772Sian	struct simplebus_softc *sc;
169280772Sian
170280772Sian	sc = device_get_softc(dev);
171280772Sian	if (node == 0)
172280772Sian		node = ofw_bus_get_node(dev);
173261352Snwhitehorn	sc->dev = dev;
174261352Snwhitehorn	sc->node = node;
175261352Snwhitehorn
176208747Sraj	/*
177261352Snwhitehorn	 * Some important numbers
178208747Sraj	 */
179261352Snwhitehorn	sc->acells = 2;
180261352Snwhitehorn	OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells));
181261352Snwhitehorn	sc->scells = 1;
182261352Snwhitehorn	OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells));
183261352Snwhitehorn}
184208747Sraj
185261352Snwhitehornstatic int
186261352Snwhitehornsimplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc)
187261352Snwhitehorn{
188261352Snwhitehorn	int host_address_cells;
189261352Snwhitehorn	cell_t *base_ranges;
190261352Snwhitehorn	ssize_t nbase_ranges;
191261352Snwhitehorn	int err;
192261352Snwhitehorn	int i, j, k;
193261352Snwhitehorn
194261352Snwhitehorn	err = OF_searchencprop(OF_parent(node), "#address-cells",
195261352Snwhitehorn	    &host_address_cells, sizeof(host_address_cells));
196261352Snwhitehorn	if (err <= 0)
197261352Snwhitehorn		return (-1);
198261352Snwhitehorn
199261352Snwhitehorn	nbase_ranges = OF_getproplen(node, "ranges");
200261352Snwhitehorn	if (nbase_ranges < 0)
201261352Snwhitehorn		return (-1);
202261352Snwhitehorn	sc->nranges = nbase_ranges / sizeof(cell_t) /
203261352Snwhitehorn	    (sc->acells + host_address_cells + sc->scells);
204261352Snwhitehorn	if (sc->nranges == 0)
205261352Snwhitehorn		return (0);
206261352Snwhitehorn
207261352Snwhitehorn	sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
208261352Snwhitehorn	    M_DEVBUF, M_WAITOK);
209261352Snwhitehorn	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
210261352Snwhitehorn	OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
211261352Snwhitehorn
212261352Snwhitehorn	for (i = 0, j = 0; i < sc->nranges; i++) {
213261352Snwhitehorn		sc->ranges[i].bus = 0;
214261352Snwhitehorn		for (k = 0; k < sc->acells; k++) {
215261352Snwhitehorn			sc->ranges[i].bus <<= 32;
216261352Snwhitehorn			sc->ranges[i].bus |= base_ranges[j++];
217208747Sraj		}
218261352Snwhitehorn		sc->ranges[i].host = 0;
219261352Snwhitehorn		for (k = 0; k < host_address_cells; k++) {
220261352Snwhitehorn			sc->ranges[i].host <<= 32;
221261352Snwhitehorn			sc->ranges[i].host |= base_ranges[j++];
222261352Snwhitehorn		}
223261352Snwhitehorn		sc->ranges[i].size = 0;
224261352Snwhitehorn		for (k = 0; k < sc->scells; k++) {
225261352Snwhitehorn			sc->ranges[i].size <<= 32;
226261352Snwhitehorn			sc->ranges[i].size |= base_ranges[j++];
227261352Snwhitehorn		}
228208747Sraj	}
229208747Sraj
230261352Snwhitehorn	free(base_ranges, M_DEVBUF);
231261352Snwhitehorn	return (sc->nranges);
232208747Sraj}
233208747Sraj
234280772Sianstruct simplebus_devinfo *
235280772Siansimplebus_setup_dinfo(device_t dev, phandle_t node,
236280772Sian    struct simplebus_devinfo *di)
237208747Sraj{
238261352Snwhitehorn	struct simplebus_softc *sc;
239261352Snwhitehorn	struct simplebus_devinfo *ndi;
240208747Sraj
241261352Snwhitehorn	sc = device_get_softc(dev);
242280772Sian	if (di == NULL)
243280772Sian		ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
244280772Sian	else
245280772Sian		ndi = di;
246261352Snwhitehorn	if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) {
247280772Sian		if (di == NULL)
248280772Sian			free(ndi, M_DEVBUF);
249261352Snwhitehorn		return (NULL);
250261352Snwhitehorn	}
251208747Sraj
252261352Snwhitehorn	resource_list_init(&ndi->rl);
253277098Szbb	ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, &ndi->rl);
254301453Sskra#ifndef INTRNG
255282972Sbr	ofw_bus_intr_to_rl(dev, node, &ndi->rl, NULL);
256301453Sskra#endif
257261352Snwhitehorn
258261352Snwhitehorn	return (ndi);
259208747Sraj}
260208747Sraj
261280772Siandevice_t
262280772Siansimplebus_add_device(device_t dev, phandle_t node, u_int order,
263280772Sian    const char *name, int unit, struct simplebus_devinfo *di)
264280772Sian{
265280772Sian	struct simplebus_devinfo *ndi;
266280772Sian	device_t cdev;
267280772Sian
268280772Sian	if ((ndi = simplebus_setup_dinfo(dev, node, di)) == NULL)
269280772Sian		return (NULL);
270280772Sian	cdev = device_add_child_ordered(dev, order, name, unit);
271280772Sian	if (cdev == NULL) {
272280772Sian		device_printf(dev, "<%s>: device_add_child failed\n",
273280772Sian		    ndi->obdinfo.obd_name);
274280772Sian		resource_list_free(&ndi->rl);
275280772Sian		ofw_bus_gen_destroy_devinfo(&ndi->obdinfo);
276280772Sian		if (di == NULL)
277280772Sian			free(ndi, M_DEVBUF);
278280772Sian		return (NULL);
279280772Sian	}
280280772Sian	device_set_ivars(cdev, ndi);
281280772Sian
282280772Sian	return(cdev);
283280772Sian}
284280772Sian
285280772Sianstatic device_t
286280772Siansimplebus_add_child(device_t dev, u_int order, const char *name, int unit)
287280772Sian{
288280772Sian	device_t cdev;
289280772Sian	struct simplebus_devinfo *ndi;
290280772Sian
291280772Sian	cdev = device_add_child_ordered(dev, order, name, unit);
292280772Sian	if (cdev == NULL)
293280772Sian		return (NULL);
294280772Sian
295280772Sian	ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
296280772Sian	ndi->obdinfo.obd_node = -1;
297280772Sian	resource_list_init(&ndi->rl);
298280772Sian	device_set_ivars(cdev, ndi);
299280772Sian
300280772Sian	return (cdev);
301280772Sian}
302280772Sian
303261352Snwhitehornstatic const struct ofw_bus_devinfo *
304261352Snwhitehornsimplebus_get_devinfo(device_t bus __unused, device_t child)
305261352Snwhitehorn{
306261352Snwhitehorn        struct simplebus_devinfo *ndi;
307261352Snwhitehorn
308261352Snwhitehorn        ndi = device_get_ivars(child);
309292159Smmel	if (ndi == NULL)
310292159Smmel		return (NULL);
311261352Snwhitehorn        return (&ndi->obdinfo);
312261352Snwhitehorn}
313261352Snwhitehorn
314280772Sianstatic struct resource_list *
315280772Siansimplebus_get_resource_list(device_t bus __unused, device_t child)
316280772Sian{
317280772Sian	struct simplebus_devinfo *ndi;
318280772Sian
319280772Sian	ndi = device_get_ivars(child);
320292159Smmel	if (ndi == NULL)
321292159Smmel		return (NULL);
322280772Sian	return (&ndi->rl);
323280772Sian}
324280772Sian
325208747Srajstatic struct resource *
326208747Srajsimplebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
327294883Sjhibbits    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
328208747Sraj{
329261352Snwhitehorn	struct simplebus_softc *sc;
330208747Sraj	struct simplebus_devinfo *di;
331208747Sraj	struct resource_list_entry *rle;
332261352Snwhitehorn	int j;
333208747Sraj
334261352Snwhitehorn	sc = device_get_softc(bus);
335261352Snwhitehorn
336208747Sraj	/*
337208747Sraj	 * Request for the default allocation with a given rid: use resource
338208747Sraj	 * list stored in the local device info.
339208747Sraj	 */
340295832Sjhibbits	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
341208747Sraj		if ((di = device_get_ivars(child)) == NULL)
342208747Sraj			return (NULL);
343208747Sraj
344208747Sraj		if (type == SYS_RES_IOPORT)
345208747Sraj			type = SYS_RES_MEMORY;
346208747Sraj
347261352Snwhitehorn		rle = resource_list_find(&di->rl, type, *rid);
348208747Sraj		if (rle == NULL) {
349248481Sray			if (bootverbose)
350248481Sray				device_printf(bus, "no default resources for "
351248481Sray				    "rid = %d, type = %d\n", *rid, type);
352208747Sraj			return (NULL);
353208747Sraj		}
354208747Sraj		start = rle->start;
355208747Sraj		end = rle->end;
356208747Sraj		count = rle->count;
357261352Snwhitehorn        }
358261352Snwhitehorn
359261352Snwhitehorn	if (type == SYS_RES_MEMORY) {
360261352Snwhitehorn		/* Remap through ranges property */
361261352Snwhitehorn		for (j = 0; j < sc->nranges; j++) {
362261352Snwhitehorn			if (start >= sc->ranges[j].bus && end <
363261352Snwhitehorn			    sc->ranges[j].bus + sc->ranges[j].size) {
364261352Snwhitehorn				start -= sc->ranges[j].bus;
365261352Snwhitehorn				start += sc->ranges[j].host;
366261352Snwhitehorn				end -= sc->ranges[j].bus;
367261352Snwhitehorn				end += sc->ranges[j].host;
368261352Snwhitehorn				break;
369261352Snwhitehorn			}
370261352Snwhitehorn		}
371261352Snwhitehorn		if (j == sc->nranges && sc->nranges != 0) {
372261352Snwhitehorn			if (bootverbose)
373261352Snwhitehorn				device_printf(bus, "Could not map resource "
374297000Sjhibbits				    "%#jx-%#jx\n", start, end);
375261352Snwhitehorn
376261352Snwhitehorn			return (NULL);
377261352Snwhitehorn		}
378208747Sraj	}
379208747Sraj
380208747Sraj	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
381208747Sraj	    count, flags));
382208747Sraj}
383208747Sraj
384256861Sbrooksstatic int
385261352Snwhitehornsimplebus_print_res(struct simplebus_devinfo *di)
386256861Sbrooks{
387261352Snwhitehorn	int rv;
388256861Sbrooks
389292159Smmel	if (di == NULL)
390292159Smmel		return (0);
391261352Snwhitehorn	rv = 0;
392297000Sjhibbits	rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#jx");
393297000Sjhibbits	rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%jd");
394261352Snwhitehorn	return (rv);
395256861Sbrooks}
396256861Sbrooks
397261352Snwhitehornstatic void
398261352Snwhitehornsimplebus_probe_nomatch(device_t bus, device_t child)
399256861Sbrooks{
400261791Simp	const char *name, *type, *compat;
401256861Sbrooks
402261352Snwhitehorn	if (!bootverbose)
403261352Snwhitehorn		return;
404256861Sbrooks
405280772Sian	compat = ofw_bus_get_compat(child);
406280772Sian	if (compat == NULL)
407280772Sian		return;
408261352Snwhitehorn	name = ofw_bus_get_name(child);
409261352Snwhitehorn	type = ofw_bus_get_type(child);
410256861Sbrooks
411261352Snwhitehorn	device_printf(bus, "<%s>", name != NULL ? name : "unknown");
412261352Snwhitehorn	simplebus_print_res(device_get_ivars(child));
413261791Simp	if (!ofw_bus_status_okay(child))
414261791Simp		printf(" disabled");
415261791Simp	if (type)
416261791Simp		printf(" type %s", type);
417280772Sian	printf(" compat %s (no driver attached)\n", compat);
418256861Sbrooks}
419256861Sbrooks
420208747Srajstatic int
421261352Snwhitehornsimplebus_print_child(device_t bus, device_t child)
422208747Sraj{
423261352Snwhitehorn	int rv;
424208747Sraj
425261352Snwhitehorn	rv = bus_print_child_header(bus, child);
426261352Snwhitehorn	rv += simplebus_print_res(device_get_ivars(child));
427261791Simp	if (!ofw_bus_status_okay(child))
428261791Simp		rv += printf(" disabled");
429261352Snwhitehorn	rv += bus_print_child_footer(bus, child);
430261352Snwhitehorn	return (rv);
431208747Sraj}
432