simplebus.c revision 248480
1/*-
2 * Copyright (c) 2009-2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Semihalf under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/fdt/simplebus.c 248480 2013-03-18 23:35:01Z ray $");
32
33#include "opt_platform.h"
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/ktr.h>
37#include <sys/kernel.h>
38#include <sys/module.h>
39#include <sys/bus.h>
40#include <sys/rman.h>
41#include <sys/malloc.h>
42
43#include <machine/fdt.h>
44
45#include <dev/ofw/ofw_bus.h>
46#include <dev/ofw/ofw_bus_subr.h>
47#include <dev/ofw/openfirm.h>
48
49#include "fdt_common.h"
50#include "ofw_bus_if.h"
51
52#ifdef DEBUG
53#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
54    printf(fmt,##args); } while (0)
55#else
56#define debugf(fmt, args...)
57#endif
58
59static MALLOC_DEFINE(M_SIMPLEBUS, "simplebus", "simplebus devices information");
60
61struct simplebus_softc {
62	int	sc_addr_cells;
63	int	sc_size_cells;
64};
65
66struct simplebus_devinfo {
67	struct ofw_bus_devinfo	di_ofw;
68	struct resource_list	di_res;
69
70	/* Interrupts sense-level info for this device */
71	struct fdt_sense_level	di_intr_sl[DI_MAX_INTR_NUM];
72};
73
74/*
75 * Prototypes.
76 */
77static int simplebus_probe(device_t);
78static int simplebus_attach(device_t);
79
80static int simplebus_print_child(device_t, device_t);
81static int simplebus_setup_intr(device_t, device_t, struct resource *, int,
82    driver_filter_t *, driver_intr_t *, void *, void **);
83
84static struct resource *simplebus_alloc_resource(device_t, device_t, int,
85    int *, u_long, u_long, u_long, u_int);
86static struct resource_list *simplebus_get_resource_list(device_t, device_t);
87
88static ofw_bus_get_devinfo_t simplebus_get_devinfo;
89
90/*
91 * Bus interface definition.
92 */
93static device_method_t simplebus_methods[] = {
94	/* Device interface */
95	DEVMETHOD(device_probe,		simplebus_probe),
96	DEVMETHOD(device_attach,	simplebus_attach),
97	DEVMETHOD(device_detach,	bus_generic_detach),
98	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
99	DEVMETHOD(device_suspend,	bus_generic_suspend),
100	DEVMETHOD(device_resume,	bus_generic_resume),
101
102	/* Bus interface */
103	DEVMETHOD(bus_print_child,	simplebus_print_child),
104	DEVMETHOD(bus_alloc_resource,	simplebus_alloc_resource),
105	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
106	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
107	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
108	DEVMETHOD(bus_setup_intr,	simplebus_setup_intr),
109	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
110	DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list),
111
112	/* OFW bus interface */
113	DEVMETHOD(ofw_bus_get_devinfo,	simplebus_get_devinfo),
114	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
115	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
116	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
117	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
118	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
119
120	{ 0, 0 }
121};
122
123static driver_t simplebus_driver = {
124	"simplebus",
125	simplebus_methods,
126	sizeof(struct simplebus_softc)
127};
128
129devclass_t simplebus_devclass;
130
131DRIVER_MODULE(simplebus, fdtbus, simplebus_driver, simplebus_devclass, 0, 0);
132
133static int
134simplebus_probe(device_t dev)
135{
136
137	if (!ofw_bus_is_compatible(dev, "simple-bus"))
138		return (ENXIO);
139
140	device_set_desc(dev, "Flattened device tree simple bus");
141
142	return (BUS_PROBE_GENERIC);
143}
144
145static int
146simplebus_attach(device_t dev)
147{
148	device_t dev_child;
149	struct simplebus_devinfo *di;
150	struct simplebus_softc *sc;
151	phandle_t dt_node, dt_child;
152
153	sc = device_get_softc(dev);
154
155	/*
156	 * Walk simple-bus and add direct subordinates as our children.
157	 */
158	dt_node = ofw_bus_get_node(dev);
159	for (dt_child = OF_child(dt_node); dt_child != 0;
160	    dt_child = OF_peer(dt_child)) {
161
162		/* Check and process 'status' property. */
163		if (!(fdt_is_enabled(dt_child)))
164			continue;
165
166		if (!(fdt_pm_is_enabled(dt_child)))
167			continue;
168
169		di = malloc(sizeof(*di), M_SIMPLEBUS, M_WAITOK | M_ZERO);
170
171		if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) {
172			free(di, M_SIMPLEBUS);
173			device_printf(dev, "could not set up devinfo\n");
174			continue;
175		}
176
177		resource_list_init(&di->di_res);
178		if (fdt_reg_to_rl(dt_child, &di->di_res)) {
179			device_printf(dev,
180			    "%s: could not process 'reg' "
181			    "property\n", di->di_ofw.obd_name);
182			ofw_bus_gen_destroy_devinfo(&di->di_ofw);
183			free(di, M_SIMPLEBUS);
184			continue;
185		}
186
187		if (fdt_intr_to_rl(dt_child, &di->di_res, di->di_intr_sl)) {
188			device_printf(dev, "%s: could not process "
189			    "'interrupts' property\n", di->di_ofw.obd_name);
190			resource_list_free(&di->di_res);
191			ofw_bus_gen_destroy_devinfo(&di->di_ofw);
192			free(di, M_SIMPLEBUS);
193			continue;
194		}
195
196		/* Add newbus device for this FDT node */
197		dev_child = device_add_child(dev, NULL, -1);
198		if (dev_child == NULL) {
199			device_printf(dev, "could not add child: %s\n",
200			    di->di_ofw.obd_name);
201			resource_list_free(&di->di_res);
202			ofw_bus_gen_destroy_devinfo(&di->di_ofw);
203			free(di, M_SIMPLEBUS);
204			continue;
205		}
206#ifdef DEBUG
207		device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name);
208#endif
209		device_set_ivars(dev_child, di);
210	}
211
212	return (bus_generic_attach(dev));
213}
214
215static int
216simplebus_print_child(device_t dev, device_t child)
217{
218	struct simplebus_devinfo *di;
219	struct resource_list *rl;
220	int rv;
221
222	di = device_get_ivars(child);
223	rl = &di->di_res;
224
225	rv = 0;
226	rv += bus_print_child_header(dev, child);
227	rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
228	rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
229	rv += bus_print_child_footer(dev, child);
230
231	return (rv);
232}
233
234static struct resource *
235simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid,
236    u_long start, u_long end, u_long count, u_int flags)
237{
238	struct simplebus_devinfo *di;
239	struct resource_list_entry *rle;
240
241	/*
242	 * Request for the default allocation with a given rid: use resource
243	 * list stored in the local device info.
244	 */
245	if ((start == 0UL) && (end == ~0UL)) {
246		if ((di = device_get_ivars(child)) == NULL)
247			return (NULL);
248
249		if (type == SYS_RES_IOPORT)
250			type = SYS_RES_MEMORY;
251
252		rle = resource_list_find(&di->di_res, type, *rid);
253		if (rle == NULL) {
254			device_printf(bus, "no default resources for "
255			    "rid = %d, type = %d\n", *rid, type);
256			return (NULL);
257		}
258		start = rle->start;
259		end = rle->end;
260		count = rle->count;
261	}
262
263	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
264	    count, flags));
265}
266
267static struct resource_list *
268simplebus_get_resource_list(device_t bus, device_t child)
269{
270	struct simplebus_devinfo *di;
271
272	di = device_get_ivars(child);
273	return (&di->di_res);
274}
275
276static int
277simplebus_setup_intr(device_t bus, device_t child, struct resource *res,
278    int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg,
279    void **cookiep)
280{
281	struct simplebus_devinfo *di;
282	enum intr_trigger trig;
283	enum intr_polarity pol;
284	int error, rid;
285
286	if (device_get_parent(child) != bus)
287		return (ECHILD);
288
289	di = device_get_ivars(child);
290	if (di == NULL)
291		return (ENXIO);
292
293	if (res == NULL)
294		return (EINVAL);
295
296	rid = rman_get_rid(res);
297	if (rid >= DI_MAX_INTR_NUM)
298		return (ENOENT);
299
300	trig = di->di_intr_sl[rid].trig;
301	pol = di->di_intr_sl[rid].pol;
302	if (trig != INTR_TRIGGER_CONFORM || pol != INTR_POLARITY_CONFORM) {
303		error = bus_generic_config_intr(bus, rman_get_start(res),
304		    trig, pol);
305		if (error)
306			return (error);
307	}
308
309	error = bus_generic_setup_intr(bus, child, res, flags, filter, ihand,
310	    arg, cookiep);
311	return (error);
312}
313
314static const struct ofw_bus_devinfo *
315simplebus_get_devinfo(device_t bus, device_t child)
316{
317	struct simplebus_devinfo *di;
318
319	di = device_get_ivars(child);
320	return (&di->di_ofw);
321}
322