nexus.c revision 146474
1/*-
2 * Copyright 1998 Massachusetts Institute of Technology
3 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.
4 * All rights reserved.
5 *
6 * Permission to use, copy, modify, and distribute this software and
7 * its documentation for any purpose and without fee is hereby
8 * granted, provided that both the above copyright notice and this
9 * permission notice appear in all copies, that both the above
10 * copyright notice and this permission notice appear in all
11 * supporting documentation, and that the name of M.I.T. not be used
12 * in advertising or publicity pertaining to distribution of the
13 * software without specific, written prior permission.  M.I.T. makes
14 * no representations about the suitability of this software for any
15 * purpose.  It is provided "as is" without express or implied
16 * warranty.
17 *
18 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
19 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
22 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * 	from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/nexus.c 146474 2005-05-21 20:19:27Z marius $");
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/bus.h>
40#include <sys/cons.h>
41#include <sys/kernel.h>
42#include <sys/malloc.h>
43#include <sys/module.h>
44
45#include <dev/ofw/openfirm.h>
46
47#include <machine/bus.h>
48#include <machine/frame.h>
49#include <machine/intr_machdep.h>
50#include <machine/nexusvar.h>
51#include <machine/ofw_upa.h>
52#include <machine/resource.h>
53#include <machine/upa.h>
54
55#include <sys/rman.h>
56
57/*
58 * The nexus (which is a pseudo-bus actually) iterates over the nodes that
59 * hang from the Open Firmware root node and adds them as devices to this bus
60 * (except some special nodes which are excluded) so that drivers can be
61 * attached to them.
62 *
63 * Additionally, interrupt setup/teardown and some resource management are
64 * done at this level.
65 *
66 * Maybe this code should get into dev/ofw to some extent, as some of it should
67 * work for all Open Firmware based machines...
68 */
69
70static MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
71
72struct nexus_devinfo {
73	phandle_t	ndi_node;
74	/* Some common properties. */
75	char		*ndi_name;
76	char		*ndi_device_type;
77	char		*ndi_model;
78	struct		upa_regs *ndi_reg;
79	int		ndi_nreg;
80	u_int		*ndi_interrupts;
81	int		ndi_ninterrupts;
82};
83
84struct nexus_softc {
85	struct rman	sc_intr_rman;
86	struct rman	sc_mem_rman;
87};
88
89static device_probe_t nexus_probe;
90static device_attach_t nexus_attach;
91static bus_add_child_t nexus_add_child;
92static bus_probe_nomatch_t nexus_probe_nomatch;
93static bus_read_ivar_t nexus_read_ivar;
94static bus_setup_intr_t nexus_setup_intr;
95static bus_teardown_intr_t nexus_teardown_intr;
96static bus_alloc_resource_t nexus_alloc_resource;
97static bus_activate_resource_t nexus_activate_resource;
98static bus_deactivate_resource_t nexus_deactivate_resource;
99static bus_release_resource_t nexus_release_resource;
100
101static device_method_t nexus_methods[] = {
102	/* Device interface */
103	DEVMETHOD(device_probe,		nexus_probe),
104	DEVMETHOD(device_attach,	nexus_attach),
105	DEVMETHOD(device_detach,	bus_generic_detach),
106	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
107	DEVMETHOD(device_suspend,	bus_generic_suspend),
108	DEVMETHOD(device_resume,	bus_generic_resume),
109
110	/* Bus interface. */
111	DEVMETHOD(bus_add_child,	nexus_add_child),
112	DEVMETHOD(bus_print_child,	bus_generic_print_child),
113	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
114	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
115	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
116	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
117	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
118	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
119	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
120	DEVMETHOD(bus_release_resource,	nexus_release_resource),
121
122	{ 0, 0 }
123};
124
125static driver_t nexus_driver = {
126	"nexus",
127	nexus_methods,
128	sizeof(struct nexus_softc),
129};
130
131static devclass_t nexus_devclass;
132
133DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
134
135static char *nexus_excl_name[] = {
136	"aliases",
137	"chosen",
138	"counter-timer",	/* No separate device; handled by psycho/sbus */
139	"memory",
140	"openprom",
141	"options",
142	"packages",
143	"virtual-memory",
144	NULL
145};
146
147static char *nexus_excl_type[] = {
148	"cpu",
149	NULL
150};
151
152extern struct bus_space_tag nexus_bustag;
153extern struct bus_dma_tag nexus_dmatag;
154
155static int
156nexus_inlist(char *name, char *list[])
157{
158	int i;
159
160	for (i = 0; list[i] != NULL; i++)
161		if (strcmp(name, list[i]) == 0)
162			return (1);
163	return (0);
164}
165
166#define	NEXUS_EXCLUDED(name, type)					\
167	(nexus_inlist((name), nexus_excl_name) ||			\
168	((type) != NULL && nexus_inlist((type), nexus_excl_type)))
169
170static int
171nexus_probe(device_t dev)
172{
173
174	/* Nexus does always match. */
175	device_set_desc(dev, "Open Firmware Nexus device");
176	return (0);
177}
178
179static int
180nexus_attach(device_t dev)
181{
182	phandle_t root;
183	phandle_t child;
184	device_t cdev;
185	struct nexus_devinfo *dinfo;
186	struct nexus_softc *sc;
187	char *name, *type;
188
189	if ((root = OF_peer(0)) == -1)
190		panic("nexus_probe: OF_peer failed.");
191
192	sc = device_get_softc(dev);
193	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
194	sc->sc_intr_rman.rm_descr = "Interrupts";
195	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
196	sc->sc_mem_rman.rm_descr = "UPA Device Memory";
197	if (rman_init(&sc->sc_intr_rman) != 0 ||
198	    rman_init(&sc->sc_mem_rman) != 0 ||
199	    rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 ||
200	    rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0)
201		panic("nexus_attach(): failed to set up rmans");
202
203	/*
204	 * Allow devices to identify.
205	 */
206	bus_generic_probe(dev);
207
208	/*
209	 * Now walk the OFW tree and attach top-level devices.
210	 */
211	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
212		if (child == -1)
213			panic("nexus_attach(): OF_child() failed.");
214		if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1)
215			continue;
216		OF_getprop_alloc(child, "device_type", 1, (void **)&type);
217		if (NEXUS_EXCLUDED(name, type)) {
218			free(name, M_OFWPROP);
219			free(type, M_OFWPROP);
220			continue;
221		}
222		cdev = device_add_child(dev, NULL, -1);
223		if (cdev == NULL)
224			panic("nexus_attach(): device_add_child() failed.");
225		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
226		dinfo->ndi_node = child;
227		dinfo->ndi_name = name;
228		dinfo->ndi_device_type = type;
229		OF_getprop_alloc(child, "model", 1,
230		    (void **)&dinfo->ndi_model);
231		dinfo->ndi_nreg = OF_getprop_alloc(child, "reg",
232		    sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg);
233		dinfo->ndi_ninterrupts = OF_getprop_alloc(child,
234		    "interrupts", sizeof(*dinfo->ndi_interrupts),
235		    (void **)&dinfo->ndi_interrupts);
236		device_set_ivars(cdev, dinfo);
237	}
238	return (bus_generic_attach(dev));
239}
240
241static device_t
242nexus_add_child(device_t dev, int order, const char *name, int unit)
243{
244	device_t cdev;
245	struct nexus_devinfo *dinfo;
246
247	cdev = device_add_child_ordered(dev, order, name, unit);
248	if (cdev == NULL)
249		return (NULL);
250
251	dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_NOWAIT | M_ZERO);
252	if (dinfo == NULL)
253		return (NULL);
254
255	dinfo->ndi_node = -1;
256	dinfo->ndi_name = strdup(name, M_OFWPROP);
257	device_set_ivars(cdev, dinfo);
258
259	return (cdev);
260}
261
262static void
263nexus_probe_nomatch(device_t dev, device_t child)
264{
265	char *type;
266
267	if ((type = nexus_get_device_type(child)) == NULL)
268		type = "(unknown)";
269	device_printf(dev, "<%s>, type %s (no driver attached)\n",
270	    nexus_get_name(child), type);
271}
272
273static int
274nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
275{
276	struct nexus_devinfo *dinfo;
277
278	if ((dinfo = device_get_ivars(child)) == 0)
279		return (ENOENT);
280	switch (which) {
281	case NEXUS_IVAR_NODE:
282		*result = dinfo->ndi_node;
283		break;
284	case NEXUS_IVAR_NAME:
285		*result = (uintptr_t)dinfo->ndi_name;
286		break;
287	case NEXUS_IVAR_DEVICE_TYPE:
288		*result = (uintptr_t)dinfo->ndi_device_type;
289		break;
290	case NEXUS_IVAR_MODEL:
291		*result = (uintptr_t)dinfo->ndi_model;
292		break;
293	case NEXUS_IVAR_REG:
294		*result = (uintptr_t)dinfo->ndi_reg;
295		break;
296	case NEXUS_IVAR_NREG:
297		*result = dinfo->ndi_nreg;
298		break;
299	case NEXUS_IVAR_INTERRUPTS:
300		*result = (uintptr_t)dinfo->ndi_interrupts;
301		break;
302	case NEXUS_IVAR_NINTERRUPTS:
303		*result = dinfo->ndi_ninterrupts;
304		break;
305	case NEXUS_IVAR_DMATAG:
306		*result = (uintptr_t)&nexus_dmatag;
307		break;
308	default:
309		return (ENOENT);
310	}
311	return 0;
312}
313
314static int
315nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
316    driver_intr_t *intr, void *arg, void **cookiep)
317{
318	int error;
319
320	if (res == NULL)
321		panic("nexus_setup_intr: NULL interrupt resource!");
322
323	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
324		flags |= INTR_EXCL;
325
326	/* We depend here on rman_activate_resource() being idempotent. */
327	error = rman_activate_resource(res);
328	if (error)
329		return (error);
330
331	error = inthand_add(device_get_nameunit(child), rman_get_start(res),
332	    intr, arg, flags, cookiep);
333
334	return (error);
335}
336
337static int
338nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
339{
340
341	inthand_remove(rman_get_start(r), ih);
342	return (0);
343}
344
345static struct resource *
346nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
347    u_long start, u_long end, u_long count, u_int flags)
348{
349	struct	nexus_softc *sc = device_get_softc(bus);
350	struct	resource *rv;
351	struct	rman *rm;
352	int needactivate = flags & RF_ACTIVE;
353
354	flags &= ~RF_ACTIVE;
355
356	switch (type) {
357	case SYS_RES_IRQ:
358		rm = &sc->sc_intr_rman;
359		break;
360	case SYS_RES_MEMORY:
361		rm = &sc->sc_mem_rman;
362		break;
363	default:
364		return (NULL);
365	}
366
367	rv = rman_reserve_resource(rm, start, end, count, flags, child);
368	if (rv == NULL)
369		return (NULL);
370	if (type == SYS_RES_MEMORY) {
371		rman_set_bustag(rv, &nexus_bustag);
372		rman_set_bushandle(rv, rman_get_start(rv));
373	}
374
375	if (needactivate) {
376		if (bus_activate_resource(child, type, *rid, rv) != 0) {
377			rman_release_resource(rv);
378			return (NULL);
379		}
380	}
381
382	return (rv);
383}
384
385static int
386nexus_activate_resource(device_t bus, device_t child, int type, int rid,
387    struct resource *r)
388{
389
390	/* Not much to be done yet... */
391	return (rman_activate_resource(r));
392}
393
394static int
395nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
396    struct resource *r)
397{
398
399	/* Not much to be done yet... */
400	return (rman_deactivate_resource(r));
401}
402
403static int
404nexus_release_resource(device_t bus, device_t child, int type, int rid,
405		       struct resource *r)
406{
407	int error;
408
409	if (rman_get_flags(r) & RF_ACTIVE) {
410		error = bus_deactivate_resource(child, type, rid, r);
411		if (error)
412			return (error);
413	}
414	return (rman_release_resource(r));
415}
416