nexus.c revision 133862
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 * $FreeBSD: head/sys/sparc64/sparc64/nexus.c 133862 2004-08-16 15:45:27Z marius $
34 */
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/bus.h>
39#include <sys/cons.h>
40#include <sys/kernel.h>
41#include <sys/malloc.h>
42#include <sys/module.h>
43
44#include <dev/ofw/openfirm.h>
45
46#include <machine/bus.h>
47#include <machine/frame.h>
48#include <machine/intr_machdep.h>
49#include <machine/nexusvar.h>
50#include <machine/ofw_upa.h>
51#include <machine/resource.h>
52#include <machine/upa.h>
53
54#include <sys/rman.h>
55
56/*
57 * The nexus (which is a pseudo-bus actually) iterates over the nodes that
58 * hang from the Open Firmware root node and adds them as devices to this bus
59 * (except some special nodes which are excluded) so that drivers can be
60 * attached to them.
61 *
62 * Additionally, interrupt setup/teardown and some resource management are
63 * done at this level.
64 *
65 * Maybe this code should get into dev/ofw to some extent, as some of it should
66 * work for all Open Firmware based machines...
67 */
68
69static MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
70
71struct nexus_devinfo {
72	phandle_t	ndi_node;
73	/* Some common properties. */
74	char		*ndi_name;
75	char		*ndi_device_type;
76	char		*ndi_model;
77	struct		upa_regs *ndi_reg;
78	int		ndi_nreg;
79	u_int		*ndi_interrupts;
80	int		ndi_ninterrupts;
81};
82
83struct nexus_softc {
84	struct rman	sc_intr_rman;
85	struct rman	sc_mem_rman;
86};
87
88static device_probe_t nexus_probe;
89static device_attach_t nexus_attach;
90static bus_probe_nomatch_t nexus_probe_nomatch;
91static bus_read_ivar_t nexus_read_ivar;
92static bus_setup_intr_t nexus_setup_intr;
93static bus_teardown_intr_t nexus_teardown_intr;
94static bus_alloc_resource_t nexus_alloc_resource;
95static bus_activate_resource_t nexus_activate_resource;
96static bus_deactivate_resource_t nexus_deactivate_resource;
97static bus_release_resource_t nexus_release_resource;
98
99static device_method_t nexus_methods[] = {
100	/* Device interface */
101	DEVMETHOD(device_probe,		nexus_probe),
102	DEVMETHOD(device_attach,	nexus_attach),
103	DEVMETHOD(device_detach,	bus_generic_detach),
104	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
105	DEVMETHOD(device_suspend,	bus_generic_suspend),
106	DEVMETHOD(device_resume,	bus_generic_resume),
107
108	/* Bus interface. */
109	DEVMETHOD(bus_print_child,	bus_generic_print_child),
110	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
111	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
112	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
113	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
114	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
115	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
116	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
117	DEVMETHOD(bus_release_resource,	nexus_release_resource),
118
119	{ 0, 0 }
120};
121
122static driver_t nexus_driver = {
123	"nexus",
124	nexus_methods,
125	sizeof(struct nexus_softc),
126};
127
128static devclass_t nexus_devclass;
129
130DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
131
132static char *nexus_excl_name[] = {
133	"aliases",
134	"chosen",
135	"counter-timer",	/* No separate device; handled by psycho/sbus */
136	"memory",
137	"openprom",
138	"options",
139	"packages",
140	"virtual-memory",
141	NULL
142};
143
144static char *nexus_excl_type[] = {
145	"cpu",
146	NULL
147};
148
149extern struct bus_space_tag nexus_bustag;
150extern struct bus_dma_tag nexus_dmatag;
151
152static int
153nexus_inlist(char *name, char *list[])
154{
155	int i;
156
157	for (i = 0; list[i] != NULL; i++)
158		if (strcmp(name, list[i]) == 0)
159			return (1);
160	return (0);
161}
162
163#define	NEXUS_EXCLUDED(name, type)					\
164	(nexus_inlist((name), nexus_excl_name) ||			\
165	((type) != NULL && nexus_inlist((type), nexus_excl_type)))
166
167static int
168nexus_probe(device_t dev)
169{
170
171	/* Nexus does always match. */
172	device_set_desc(dev, "Open Firmware Nexus device");
173	return (0);
174}
175
176static int
177nexus_attach(device_t dev)
178{
179	phandle_t root;
180	phandle_t child;
181	device_t cdev;
182	struct nexus_devinfo *dinfo;
183	struct nexus_softc *sc;
184	char *name, *type;
185
186	if ((root = OF_peer(0)) == -1)
187		panic("nexus_probe: OF_peer failed.");
188
189	sc = device_get_softc(dev);
190	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
191	sc->sc_intr_rman.rm_descr = "Interrupts";
192	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
193	sc->sc_mem_rman.rm_descr = "UPA Device Memory";
194	if (rman_init(&sc->sc_intr_rman) != 0 ||
195	    rman_init(&sc->sc_mem_rman) != 0 ||
196	    rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 ||
197	    rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0)
198		panic("nexus_attach(): failed to set up rmans");
199	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
200		if (child == -1)
201			panic("nexus_attach(): OF_child() failed.");
202		if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1)
203			continue;
204		OF_getprop_alloc(child, "device_type", 1, (void **)&type);
205		if (NEXUS_EXCLUDED(name, type)) {
206			free(name, M_OFWPROP);
207			free(type, M_OFWPROP);
208			continue;
209		}
210		cdev = device_add_child(dev, NULL, -1);
211		if (cdev == NULL)
212			panic("nexus_attach(): device_add_child() failed.");
213		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
214		dinfo->ndi_node = child;
215		dinfo->ndi_name = name;
216		dinfo->ndi_device_type = type;
217		OF_getprop_alloc(child, "model", 1,
218		    (void **)&dinfo->ndi_model);
219		dinfo->ndi_nreg = OF_getprop_alloc(child, "reg",
220		    sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg);
221		dinfo->ndi_ninterrupts = OF_getprop_alloc(child,
222		    "interrupts", sizeof(*dinfo->ndi_interrupts),
223		    (void **)&dinfo->ndi_interrupts);
224		device_set_ivars(cdev, dinfo);
225	}
226	return (bus_generic_attach(dev));
227}
228
229static void
230nexus_probe_nomatch(device_t dev, device_t child)
231{
232	char *type;
233
234	if ((type = nexus_get_device_type(child)) == NULL)
235		type = "(unknown)";
236	device_printf(dev, "<%s>, type %s (no driver attached)\n",
237	    nexus_get_name(child), type);
238}
239
240static int
241nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
242{
243	struct nexus_devinfo *dinfo;
244
245	if ((dinfo = device_get_ivars(child)) == 0)
246		return (ENOENT);
247	switch (which) {
248	case NEXUS_IVAR_NODE:
249		*result = dinfo->ndi_node;
250		break;
251	case NEXUS_IVAR_NAME:
252		*result = (uintptr_t)dinfo->ndi_name;
253		break;
254	case NEXUS_IVAR_DEVICE_TYPE:
255		*result = (uintptr_t)dinfo->ndi_device_type;
256		break;
257	case NEXUS_IVAR_MODEL:
258		*result = (uintptr_t)dinfo->ndi_model;
259		break;
260	case NEXUS_IVAR_REG:
261		*result = (uintptr_t)dinfo->ndi_reg;
262		break;
263	case NEXUS_IVAR_NREG:
264		*result = dinfo->ndi_nreg;
265		break;
266	case NEXUS_IVAR_INTERRUPTS:
267		*result = (uintptr_t)dinfo->ndi_interrupts;
268		break;
269	case NEXUS_IVAR_NINTERRUPTS:
270		*result = dinfo->ndi_ninterrupts;
271		break;
272	case NEXUS_IVAR_DMATAG:
273		*result = (uintptr_t)&nexus_dmatag;
274		break;
275	default:
276		return (ENOENT);
277	}
278	return 0;
279}
280
281static int
282nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
283    driver_intr_t *intr, void *arg, void **cookiep)
284{
285	int error;
286
287	if (res == NULL)
288		panic("nexus_setup_intr: NULL interrupt resource!");
289
290	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
291		flags |= INTR_EXCL;
292
293	/* We depend here on rman_activate_resource() being idempotent. */
294	error = rman_activate_resource(res);
295	if (error)
296		return (error);
297
298	error = inthand_add(device_get_nameunit(child), rman_get_start(res),
299	    intr, arg, flags, cookiep);
300
301	return (error);
302}
303
304static int
305nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
306{
307	inthand_remove(rman_get_start(r), ih);
308	return (0);
309}
310
311static struct resource *
312nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
313    u_long start, u_long end, u_long count, u_int flags)
314{
315	struct	nexus_softc *sc = device_get_softc(bus);
316	struct	resource *rv;
317	struct	rman *rm;
318	int needactivate = flags & RF_ACTIVE;
319
320	flags &= ~RF_ACTIVE;
321
322	switch (type) {
323	case SYS_RES_IRQ:
324		rm = &sc->sc_intr_rman;
325		break;
326	case SYS_RES_MEMORY:
327		rm = &sc->sc_mem_rman;
328		break;
329	default:
330		return (NULL);
331	}
332
333	rv = rman_reserve_resource(rm, start, end, count, flags, child);
334	if (rv == NULL)
335		return (NULL);
336	if (type == SYS_RES_MEMORY) {
337		rman_set_bustag(rv, &nexus_bustag);
338		rman_set_bushandle(rv, rman_get_start(rv));
339	}
340
341	if (needactivate) {
342		if (bus_activate_resource(child, type, *rid, rv) != 0) {
343			rman_release_resource(rv);
344			return (NULL);
345		}
346	}
347
348	return (rv);
349}
350
351static int
352nexus_activate_resource(device_t bus, device_t child, int type, int rid,
353    struct resource *r)
354{
355
356	/* Not much to be done yet... */
357	return (rman_activate_resource(r));
358}
359
360static int
361nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
362    struct resource *r)
363{
364
365	/* Not much to be done yet... */
366	return (rman_deactivate_resource(r));
367}
368
369static int
370nexus_release_resource(device_t bus, device_t child, int type, int rid,
371		       struct resource *r)
372{
373	int error;
374
375	if (rman_get_flags(r) & RF_ACTIVE) {
376		error = bus_deactivate_resource(child, type, rid, r);
377		if (error)
378			return (error);
379	}
380	return (rman_release_resource(r));
381}
382