nexus.c revision 131535
186227Stmm/*
286227Stmm * Copyright 1998 Massachusetts Institute of Technology
3128776Stmm * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.
4128776Stmm * All rights reserved.
586227Stmm *
686227Stmm * Permission to use, copy, modify, and distribute this software and
786227Stmm * its documentation for any purpose and without fee is hereby
886227Stmm * granted, provided that both the above copyright notice and this
986227Stmm * permission notice appear in all copies, that both the above
1086227Stmm * copyright notice and this permission notice appear in all
1186227Stmm * supporting documentation, and that the name of M.I.T. not be used
1286227Stmm * in advertising or publicity pertaining to distribution of the
1386227Stmm * software without specific, written prior permission.  M.I.T. makes
1486227Stmm * no representations about the suitability of this software for any
1586227Stmm * purpose.  It is provided "as is" without express or implied
1686227Stmm * warranty.
17128776Stmm *
1886227Stmm * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1986227Stmm * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
2086227Stmm * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2186227Stmm * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2286227Stmm * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2386227Stmm * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2486227Stmm * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2586227Stmm * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2686227Stmm * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2786227Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2886227Stmm * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2986227Stmm * SUCH DAMAGE.
3086227Stmm *
3186227Stmm * 	from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
3286227Stmm *
3386227Stmm * $FreeBSD: head/sys/sparc64/sparc64/nexus.c 131535 2004-07-03 20:48:01Z imp $
3486227Stmm */
3586227Stmm
36131376Smarius#define	__RMAN_RESOURCE_VISIBLE
3786227Stmm#include <sys/param.h>
3886227Stmm#include <sys/systm.h>
3986227Stmm#include <sys/bus.h>
4086227Stmm#include <sys/cons.h>
4186227Stmm#include <sys/kernel.h>
4286227Stmm#include <sys/malloc.h>
43130068Sphk#include <sys/module.h>
4486227Stmm
4586227Stmm#include <dev/ofw/openfirm.h>
4686227Stmm
4786227Stmm#include <machine/bus.h>
4886227Stmm#include <machine/frame.h>
4986227Stmm#include <machine/intr_machdep.h>
5086227Stmm#include <machine/nexusvar.h>
5188823Stmm#include <machine/ofw_upa.h>
5286227Stmm#include <machine/resource.h>
5388823Stmm#include <machine/upa.h>
5486227Stmm
5586227Stmm#include <sys/rman.h>
5686227Stmm
5786227Stmm/*
5886227Stmm * The nexus (which is a pseudo-bus actually) iterates over the nodes that
59128776Stmm * hang from the OpenFirmware root node and adds them as devices to this bus
6086227Stmm * (except some special nodes which are excluded) so that drivers can be
61128776Stmm * attached to them.
6286227Stmm *
6386227Stmm * Additionally, interrupt setup/teardown and some resource management are
6486227Stmm * done at this level.
6586227Stmm *
6686227Stmm * Maybe this code should get into dev/ofw to some extent, as some of it should
6786227Stmm * work for all OpenFirmware based machines...
6886227Stmm */
6986227Stmm
7086227Stmmstatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
7186227Stmm
7286227Stmmstruct nexus_devinfo {
7386227Stmm	phandle_t	ndi_node;
7486227Stmm	/* Some common properties. */
7586227Stmm	char		*ndi_name;
7686227Stmm	char		*ndi_device_type;
7786227Stmm	char		*ndi_model;
7888823Stmm	struct		upa_regs *ndi_reg;
7986227Stmm	int		ndi_nreg;
8086227Stmm	u_int		*ndi_interrupts;
8186227Stmm	int		ndi_ninterrupts;
8286227Stmm};
8386227Stmm
8488823Stmmstruct nexus_softc {
8588823Stmm	struct rman	sc_intr_rman;
8688823Stmm	struct rman	sc_mem_rman;
8788823Stmm};
8888823Stmm
89128776Stmmstatic device_probe_t nexus_probe;
90128776Stmmstatic device_attach_t nexus_attach;
91128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch;
92128776Stmmstatic bus_read_ivar_t nexus_read_ivar;
93128776Stmmstatic bus_setup_intr_t nexus_setup_intr;
94128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr;
95128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource;
96128776Stmmstatic bus_activate_resource_t nexus_activate_resource;
97128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource;
98128776Stmmstatic bus_release_resource_t nexus_release_resource;
9986227Stmm
10086227Stmmstatic device_method_t nexus_methods[] = {
10186227Stmm	/* Device interface */
10286227Stmm	DEVMETHOD(device_probe,		nexus_probe),
103128776Stmm	DEVMETHOD(device_attach,	nexus_attach),
10486227Stmm	DEVMETHOD(device_detach,	bus_generic_detach),
10586227Stmm	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10686227Stmm	DEVMETHOD(device_suspend,	bus_generic_suspend),
10786227Stmm	DEVMETHOD(device_resume,	bus_generic_resume),
10886227Stmm
109128776Stmm	/* Bus interface. */
11086227Stmm	DEVMETHOD(bus_print_child,	bus_generic_print_child),
11186227Stmm	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
11286227Stmm	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
11386227Stmm	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
11486227Stmm	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
11586227Stmm	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
11686227Stmm	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
11786227Stmm	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
11886227Stmm	DEVMETHOD(bus_release_resource,	nexus_release_resource),
11986227Stmm
12086227Stmm	{ 0, 0 }
12186227Stmm};
12286227Stmm
12386227Stmmstatic driver_t nexus_driver = {
12486227Stmm	"nexus",
12586227Stmm	nexus_methods,
12688823Stmm	sizeof(struct nexus_softc),
12786227Stmm};
12886227Stmm
12986227Stmmstatic devclass_t nexus_devclass;
13086227Stmm
13186227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
13286227Stmm
13386227Stmmstatic char *nexus_excl_name[] = {
13490622Stmm	"aliases",
13590622Stmm	"chosen",
13690622Stmm	"counter-timer",	/* No separate device; handled by psycho/sbus */
13786227Stmm	"memory",
13890622Stmm	"openprom",
13986227Stmm	"options",
14086227Stmm	"packages",
14190622Stmm	"virtual-memory",
14286227Stmm	NULL
14386227Stmm};
14486227Stmm
14586227Stmmstatic char *nexus_excl_type[] = {
14686227Stmm	"cpu",
14786227Stmm	NULL
14886227Stmm};
14986227Stmm
15086227Stmmextern struct bus_space_tag nexus_bustag;
15186227Stmmextern struct bus_dma_tag nexus_dmatag;
15286227Stmm
15386227Stmmstatic int
15486227Stmmnexus_inlist(char *name, char *list[])
15586227Stmm{
15686227Stmm	int i;
15786227Stmm
15886227Stmm	for (i = 0; list[i] != NULL; i++)
15986227Stmm		if (strcmp(name, list[i]) == 0)
16086227Stmm			return (1);
16186227Stmm	return (0);
16286227Stmm}
16386227Stmm
16486227Stmm#define	NEXUS_EXCLUDED(name, type)					\
16586227Stmm	(nexus_inlist((name), nexus_excl_name) ||			\
16686227Stmm	((type) != NULL && nexus_inlist((type), nexus_excl_type)))
16786227Stmm
16886227Stmmstatic int
16986227Stmmnexus_probe(device_t dev)
17086227Stmm{
171128776Stmm
172128776Stmm	/* Nexus does always match. */
173128776Stmm	device_set_desc(dev, "OpenFirmware Nexus device");
174128776Stmm	return (0);
175128776Stmm}
176128776Stmm
177128776Stmmstatic int
178128776Stmmnexus_attach(device_t dev)
179128776Stmm{
18086227Stmm	phandle_t root;
18186227Stmm	phandle_t child;
18286227Stmm	device_t cdev;
18386227Stmm	struct nexus_devinfo *dinfo;
18488823Stmm	struct nexus_softc *sc;
18586227Stmm	char *name, *type;
18686227Stmm
18786227Stmm	if ((root = OF_peer(0)) == -1)
18886227Stmm		panic("nexus_probe: OF_peer failed.");
18986227Stmm
19088823Stmm	sc = device_get_softc(dev);
19188823Stmm	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
19288823Stmm	sc->sc_intr_rman.rm_descr = "Interrupts";
19388823Stmm	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
19488823Stmm	sc->sc_mem_rman.rm_descr = "UPA Device Memory";
19588823Stmm	if (rman_init(&sc->sc_intr_rman) != 0 ||
19688823Stmm	    rman_init(&sc->sc_mem_rman) != 0 ||
19797265Sjake	    rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 ||
19888823Stmm	    rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0)
199128776Stmm		panic("nexus_attach(): failed to set up rmans");
20086227Stmm	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
20186227Stmm		if (child == -1)
202128776Stmm			panic("nexus_attach(): OF_child() failed.");
20386227Stmm		if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1)
20486227Stmm			continue;
20586227Stmm		OF_getprop_alloc(child, "device_type", 1, (void **)&type);
20686227Stmm		if (NEXUS_EXCLUDED(name, type)) {
20786227Stmm			free(name, M_OFWPROP);
208128776Stmm			free(type, M_OFWPROP);
20986227Stmm			continue;
21088823Stmm		}
21186227Stmm		cdev = device_add_child(dev, NULL, -1);
212128776Stmm		if (cdev == NULL)
213128776Stmm			panic("nexus_attach(): device_add_child() failed.");
214128776Stmm		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
215128776Stmm		dinfo->ndi_node = child;
216128776Stmm		dinfo->ndi_name = name;
217128776Stmm		dinfo->ndi_device_type = type;
218128776Stmm		OF_getprop_alloc(child, "model", 1,
219128776Stmm		    (void **)&dinfo->ndi_model);
220128776Stmm		dinfo->ndi_nreg = OF_getprop_alloc(child, "reg",
221128776Stmm		    sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg);
222128776Stmm		dinfo->ndi_ninterrupts = OF_getprop_alloc(child,
223128776Stmm		    "interrupts", sizeof(*dinfo->ndi_interrupts),
224128776Stmm		    (void **)&dinfo->ndi_interrupts);
225128776Stmm		device_set_ivars(cdev, dinfo);
22686227Stmm	}
227128776Stmm	return (bus_generic_attach(dev));
22886227Stmm}
22986227Stmm
23086227Stmmstatic void
23186227Stmmnexus_probe_nomatch(device_t dev, device_t child)
23286227Stmm{
23386227Stmm	char *type;
23486227Stmm
235128939Smarius	if ((type = nexus_get_device_type(child)) == NULL)
23686227Stmm		type = "(unknown)";
23786227Stmm	device_printf(dev, "<%s>, type %s (no driver attached)\n",
238128939Smarius	    nexus_get_name(child), type);
23986227Stmm}
24086227Stmm
24186227Stmmstatic int
24286227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
24386227Stmm{
24486227Stmm	struct nexus_devinfo *dinfo;
24586227Stmm
24686227Stmm	if ((dinfo = device_get_ivars(child)) == 0)
24786227Stmm		return (ENOENT);
24886227Stmm	switch (which) {
24986227Stmm	case NEXUS_IVAR_NODE:
25086227Stmm		*result = dinfo->ndi_node;
25186227Stmm		break;
25286227Stmm	case NEXUS_IVAR_NAME:
25386227Stmm		*result = (uintptr_t)dinfo->ndi_name;
25486227Stmm		break;
25586227Stmm	case NEXUS_IVAR_DEVICE_TYPE:
25686227Stmm		*result = (uintptr_t)dinfo->ndi_device_type;
25786227Stmm		break;
25886227Stmm	case NEXUS_IVAR_MODEL:
25986227Stmm		*result = (uintptr_t)dinfo->ndi_model;
26086227Stmm		break;
26186227Stmm	case NEXUS_IVAR_REG:
26286227Stmm		*result = (uintptr_t)dinfo->ndi_reg;
26386227Stmm		break;
26486227Stmm	case NEXUS_IVAR_NREG:
26586227Stmm		*result = dinfo->ndi_nreg;
26686227Stmm		break;
26786227Stmm	case NEXUS_IVAR_INTERRUPTS:
26886227Stmm		*result = (uintptr_t)dinfo->ndi_interrupts;
26986227Stmm		break;
27086227Stmm	case NEXUS_IVAR_NINTERRUPTS:
27186227Stmm		*result = dinfo->ndi_ninterrupts;
27286227Stmm		break;
27386227Stmm	case NEXUS_IVAR_DMATAG:
274128776Stmm		*result = (uintptr_t)&nexus_dmatag;
27586227Stmm		break;
27686227Stmm	default:
27786227Stmm		return (ENOENT);
27886227Stmm	}
27986227Stmm	return 0;
28086227Stmm}
28186227Stmm
28286227Stmmstatic int
28386227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
28486227Stmm    driver_intr_t *intr, void *arg, void **cookiep)
28586227Stmm{
28686227Stmm	int error;
28788823Stmm
28886227Stmm	if (res == NULL)
28986227Stmm		panic("nexus_setup_intr: NULL interrupt resource!");
29086227Stmm
291131535Simp	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
29286227Stmm		flags |= INTR_EXCL;
29386227Stmm
294128776Stmm	/* We depend here on rman_activate_resource() being idempotent. */
29586227Stmm	error = rman_activate_resource(res);
29686227Stmm	if (error)
29786227Stmm		return (error);
29886227Stmm
299131535Simp	error = inthand_add(device_get_nameunit(child), rman_get_start(res),
30086227Stmm	    intr, arg, flags, cookiep);
30186227Stmm
30286227Stmm	return (error);
30386227Stmm}
30486227Stmm
30586227Stmmstatic int
30686227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
30786227Stmm{
308131535Simp	inthand_remove(rman_get_start(r), ih);
30986227Stmm	return (0);
31086227Stmm}
31186227Stmm
31286227Stmmstatic struct resource *
31386227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
31486227Stmm    u_long start, u_long end, u_long count, u_int flags)
31586227Stmm{
31688823Stmm	struct	nexus_softc *sc = device_get_softc(bus);
31786227Stmm	struct	resource *rv;
31886227Stmm	struct	rman *rm;
31986227Stmm	int needactivate = flags & RF_ACTIVE;
32086227Stmm
32186227Stmm	flags &= ~RF_ACTIVE;
32286227Stmm
32386227Stmm	switch (type) {
32486227Stmm	case SYS_RES_IRQ:
32588823Stmm		rm = &sc->sc_intr_rman;
32686227Stmm		break;
32788823Stmm	case SYS_RES_MEMORY:
32888823Stmm		rm = &sc->sc_mem_rman;
32988823Stmm		break;
33086227Stmm	default:
33186227Stmm		return (NULL);
33286227Stmm	}
33386227Stmm
33486227Stmm	rv = rman_reserve_resource(rm, start, end, count, flags, child);
33586227Stmm	if (rv == NULL)
33686227Stmm		return (NULL);
33788823Stmm	if (type == SYS_RES_MEMORY) {
33888823Stmm		rman_set_bustag(rv, &nexus_bustag);
33988823Stmm		rman_set_bushandle(rv, rman_get_start(rv));
34088823Stmm	}
34186227Stmm
34286227Stmm	if (needactivate) {
343128776Stmm		if (bus_activate_resource(child, type, *rid, rv) != 0) {
34486227Stmm			rman_release_resource(rv);
34586227Stmm			return (NULL);
34686227Stmm		}
34786227Stmm	}
34888823Stmm
34986227Stmm	return (rv);
35086227Stmm}
35186227Stmm
35286227Stmmstatic int
35386227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid,
35486227Stmm    struct resource *r)
35586227Stmm{
35686227Stmm
35786227Stmm	/* Not much to be done yet... */
35886227Stmm	return (rman_activate_resource(r));
35986227Stmm}
36086227Stmm
36186227Stmmstatic int
36286227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
36386227Stmm    struct resource *r)
36486227Stmm{
36588823Stmm
36686227Stmm	/* Not much to be done yet... */
36786227Stmm	return (rman_deactivate_resource(r));
36886227Stmm}
36986227Stmm
37086227Stmmstatic int
37186227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid,
37286227Stmm		       struct resource *r)
37386227Stmm{
37486227Stmm	int error;
37586227Stmm
37686227Stmm	if (rman_get_flags(r) & RF_ACTIVE) {
37786227Stmm		error = bus_deactivate_resource(child, type, rid, r);
37886227Stmm		if (error)
379128776Stmm			return (error);
38086227Stmm	}
38186227Stmm	return (rman_release_resource(r));
38286227Stmm}
383