nexus.c revision 130068
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 130068 2004-06-04 11:52:25Z phk $
3486227Stmm */
3586227Stmm
3686227Stmm#include <sys/param.h>
3786227Stmm#include <sys/systm.h>
3886227Stmm#include <sys/bus.h>
3986227Stmm#include <sys/cons.h>
4086227Stmm#include <sys/kernel.h>
4186227Stmm#include <sys/malloc.h>
42130068Sphk#include <sys/module.h>
4386227Stmm
4486227Stmm#include <dev/ofw/openfirm.h>
4586227Stmm
4686227Stmm#include <machine/bus.h>
4786227Stmm#include <machine/frame.h>
4886227Stmm#include <machine/intr_machdep.h>
4986227Stmm#include <machine/nexusvar.h>
5088823Stmm#include <machine/ofw_upa.h>
5186227Stmm#include <machine/resource.h>
5288823Stmm#include <machine/upa.h>
5386227Stmm
5486227Stmm#include <sys/rman.h>
5586227Stmm
5686227Stmm/*
5786227Stmm * The nexus (which is a pseudo-bus actually) iterates over the nodes that
58128776Stmm * hang from the OpenFirmware root node and adds them as devices to this bus
5986227Stmm * (except some special nodes which are excluded) so that drivers can be
60128776Stmm * attached to them.
6186227Stmm *
6286227Stmm * Additionally, interrupt setup/teardown and some resource management are
6386227Stmm * done at this level.
6486227Stmm *
6586227Stmm * Maybe this code should get into dev/ofw to some extent, as some of it should
6686227Stmm * work for all OpenFirmware based machines...
6786227Stmm */
6886227Stmm
6986227Stmmstatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
7086227Stmm
7186227Stmmstruct nexus_devinfo {
7286227Stmm	phandle_t	ndi_node;
7386227Stmm	/* Some common properties. */
7486227Stmm	char		*ndi_name;
7586227Stmm	char		*ndi_device_type;
7686227Stmm	char		*ndi_model;
7788823Stmm	struct		upa_regs *ndi_reg;
7886227Stmm	int		ndi_nreg;
7986227Stmm	u_int		*ndi_interrupts;
8086227Stmm	int		ndi_ninterrupts;
8186227Stmm};
8286227Stmm
8388823Stmmstruct nexus_softc {
8488823Stmm	struct rman	sc_intr_rman;
8588823Stmm	struct rman	sc_mem_rman;
8688823Stmm};
8788823Stmm
88128776Stmmstatic device_probe_t nexus_probe;
89128776Stmmstatic device_attach_t nexus_attach;
90128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch;
91128776Stmmstatic bus_read_ivar_t nexus_read_ivar;
92128776Stmmstatic bus_setup_intr_t nexus_setup_intr;
93128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr;
94128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource;
95128776Stmmstatic bus_activate_resource_t nexus_activate_resource;
96128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource;
97128776Stmmstatic bus_release_resource_t nexus_release_resource;
9886227Stmm
9986227Stmmstatic device_method_t nexus_methods[] = {
10086227Stmm	/* Device interface */
10186227Stmm	DEVMETHOD(device_probe,		nexus_probe),
102128776Stmm	DEVMETHOD(device_attach,	nexus_attach),
10386227Stmm	DEVMETHOD(device_detach,	bus_generic_detach),
10486227Stmm	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10586227Stmm	DEVMETHOD(device_suspend,	bus_generic_suspend),
10686227Stmm	DEVMETHOD(device_resume,	bus_generic_resume),
10786227Stmm
108128776Stmm	/* Bus interface. */
10986227Stmm	DEVMETHOD(bus_print_child,	bus_generic_print_child),
11086227Stmm	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
11186227Stmm	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
11286227Stmm	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
11386227Stmm	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
11486227Stmm	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
11586227Stmm	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
11686227Stmm	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
11786227Stmm	DEVMETHOD(bus_release_resource,	nexus_release_resource),
11886227Stmm
11986227Stmm	{ 0, 0 }
12086227Stmm};
12186227Stmm
12286227Stmmstatic driver_t nexus_driver = {
12386227Stmm	"nexus",
12486227Stmm	nexus_methods,
12588823Stmm	sizeof(struct nexus_softc),
12686227Stmm};
12786227Stmm
12886227Stmmstatic devclass_t nexus_devclass;
12986227Stmm
13086227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
13186227Stmm
13286227Stmmstatic char *nexus_excl_name[] = {
13390622Stmm	"aliases",
13490622Stmm	"chosen",
13590622Stmm	"counter-timer",	/* No separate device; handled by psycho/sbus */
13686227Stmm	"memory",
13790622Stmm	"openprom",
13886227Stmm	"options",
13986227Stmm	"packages",
14090622Stmm	"virtual-memory",
14186227Stmm	NULL
14286227Stmm};
14386227Stmm
14486227Stmmstatic char *nexus_excl_type[] = {
14586227Stmm	"cpu",
14686227Stmm	NULL
14786227Stmm};
14886227Stmm
14986227Stmmextern struct bus_space_tag nexus_bustag;
15086227Stmmextern struct bus_dma_tag nexus_dmatag;
15186227Stmm
15286227Stmmstatic int
15386227Stmmnexus_inlist(char *name, char *list[])
15486227Stmm{
15586227Stmm	int i;
15686227Stmm
15786227Stmm	for (i = 0; list[i] != NULL; i++)
15886227Stmm		if (strcmp(name, list[i]) == 0)
15986227Stmm			return (1);
16086227Stmm	return (0);
16186227Stmm}
16286227Stmm
16386227Stmm#define	NEXUS_EXCLUDED(name, type)					\
16486227Stmm	(nexus_inlist((name), nexus_excl_name) ||			\
16586227Stmm	((type) != NULL && nexus_inlist((type), nexus_excl_type)))
16686227Stmm
16786227Stmmstatic int
16886227Stmmnexus_probe(device_t dev)
16986227Stmm{
170128776Stmm
171128776Stmm	/* Nexus does always match. */
172128776Stmm	device_set_desc(dev, "OpenFirmware Nexus device");
173128776Stmm	return (0);
174128776Stmm}
175128776Stmm
176128776Stmmstatic int
177128776Stmmnexus_attach(device_t dev)
178128776Stmm{
17986227Stmm	phandle_t root;
18086227Stmm	phandle_t child;
18186227Stmm	device_t cdev;
18286227Stmm	struct nexus_devinfo *dinfo;
18388823Stmm	struct nexus_softc *sc;
18486227Stmm	char *name, *type;
18586227Stmm
18686227Stmm	if ((root = OF_peer(0)) == -1)
18786227Stmm		panic("nexus_probe: OF_peer failed.");
18886227Stmm
18988823Stmm	sc = device_get_softc(dev);
19088823Stmm	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
19188823Stmm	sc->sc_intr_rman.rm_descr = "Interrupts";
19288823Stmm	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
19388823Stmm	sc->sc_mem_rman.rm_descr = "UPA Device Memory";
19488823Stmm	if (rman_init(&sc->sc_intr_rman) != 0 ||
19588823Stmm	    rman_init(&sc->sc_mem_rman) != 0 ||
19697265Sjake	    rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 ||
19788823Stmm	    rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0)
198128776Stmm		panic("nexus_attach(): failed to set up rmans");
19986227Stmm	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
20086227Stmm		if (child == -1)
201128776Stmm			panic("nexus_attach(): OF_child() failed.");
20286227Stmm		if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1)
20386227Stmm			continue;
20486227Stmm		OF_getprop_alloc(child, "device_type", 1, (void **)&type);
20586227Stmm		if (NEXUS_EXCLUDED(name, type)) {
20686227Stmm			free(name, M_OFWPROP);
207128776Stmm			free(type, M_OFWPROP);
20886227Stmm			continue;
20988823Stmm		}
21086227Stmm		cdev = device_add_child(dev, NULL, -1);
211128776Stmm		if (cdev == NULL)
212128776Stmm			panic("nexus_attach(): device_add_child() failed.");
213128776Stmm		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
214128776Stmm		dinfo->ndi_node = child;
215128776Stmm		dinfo->ndi_name = name;
216128776Stmm		dinfo->ndi_device_type = type;
217128776Stmm		OF_getprop_alloc(child, "model", 1,
218128776Stmm		    (void **)&dinfo->ndi_model);
219128776Stmm		dinfo->ndi_nreg = OF_getprop_alloc(child, "reg",
220128776Stmm		    sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg);
221128776Stmm		dinfo->ndi_ninterrupts = OF_getprop_alloc(child,
222128776Stmm		    "interrupts", sizeof(*dinfo->ndi_interrupts),
223128776Stmm		    (void **)&dinfo->ndi_interrupts);
224128776Stmm		device_set_ivars(cdev, dinfo);
22586227Stmm	}
226128776Stmm	return (bus_generic_attach(dev));
22786227Stmm}
22886227Stmm
22986227Stmmstatic void
23086227Stmmnexus_probe_nomatch(device_t dev, device_t child)
23186227Stmm{
23286227Stmm	char *type;
23386227Stmm
234128939Smarius	if ((type = nexus_get_device_type(child)) == NULL)
23586227Stmm		type = "(unknown)";
23686227Stmm	device_printf(dev, "<%s>, type %s (no driver attached)\n",
237128939Smarius	    nexus_get_name(child), type);
23886227Stmm}
23986227Stmm
24086227Stmmstatic int
24186227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
24286227Stmm{
24386227Stmm	struct nexus_devinfo *dinfo;
24486227Stmm
24586227Stmm	if ((dinfo = device_get_ivars(child)) == 0)
24686227Stmm		return (ENOENT);
24786227Stmm	switch (which) {
24886227Stmm	case NEXUS_IVAR_NODE:
24986227Stmm		*result = dinfo->ndi_node;
25086227Stmm		break;
25186227Stmm	case NEXUS_IVAR_NAME:
25286227Stmm		*result = (uintptr_t)dinfo->ndi_name;
25386227Stmm		break;
25486227Stmm	case NEXUS_IVAR_DEVICE_TYPE:
25586227Stmm		*result = (uintptr_t)dinfo->ndi_device_type;
25686227Stmm		break;
25786227Stmm	case NEXUS_IVAR_MODEL:
25886227Stmm		*result = (uintptr_t)dinfo->ndi_model;
25986227Stmm		break;
26086227Stmm	case NEXUS_IVAR_REG:
26186227Stmm		*result = (uintptr_t)dinfo->ndi_reg;
26286227Stmm		break;
26386227Stmm	case NEXUS_IVAR_NREG:
26486227Stmm		*result = dinfo->ndi_nreg;
26586227Stmm		break;
26686227Stmm	case NEXUS_IVAR_INTERRUPTS:
26786227Stmm		*result = (uintptr_t)dinfo->ndi_interrupts;
26886227Stmm		break;
26986227Stmm	case NEXUS_IVAR_NINTERRUPTS:
27086227Stmm		*result = dinfo->ndi_ninterrupts;
27186227Stmm		break;
27286227Stmm	case NEXUS_IVAR_DMATAG:
273128776Stmm		*result = (uintptr_t)&nexus_dmatag;
27486227Stmm		break;
27586227Stmm	default:
27686227Stmm		return (ENOENT);
27786227Stmm	}
27886227Stmm	return 0;
27986227Stmm}
28086227Stmm
28186227Stmmstatic int
28286227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
28386227Stmm    driver_intr_t *intr, void *arg, void **cookiep)
28486227Stmm{
28586227Stmm	int error;
28688823Stmm
28786227Stmm	if (res == NULL)
28886227Stmm		panic("nexus_setup_intr: NULL interrupt resource!");
28986227Stmm
29086227Stmm	if ((res->r_flags & RF_SHAREABLE) == 0)
29186227Stmm		flags |= INTR_EXCL;
29286227Stmm
293128776Stmm	/* We depend here on rman_activate_resource() being idempotent. */
29486227Stmm	error = rman_activate_resource(res);
29586227Stmm	if (error)
29686227Stmm		return (error);
29786227Stmm
29886227Stmm	error = inthand_add(device_get_nameunit(child), res->r_start,
29986227Stmm	    intr, arg, flags, cookiep);
30086227Stmm
30186227Stmm	return (error);
30286227Stmm}
30386227Stmm
30486227Stmmstatic int
30586227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
30686227Stmm{
30786227Stmm	inthand_remove(r->r_start, ih);
30886227Stmm	return (0);
30986227Stmm}
31086227Stmm
31186227Stmmstatic struct resource *
31286227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
31386227Stmm    u_long start, u_long end, u_long count, u_int flags)
31486227Stmm{
31588823Stmm	struct	nexus_softc *sc = device_get_softc(bus);
31686227Stmm	struct	resource *rv;
31786227Stmm	struct	rman *rm;
31886227Stmm	int needactivate = flags & RF_ACTIVE;
31986227Stmm
32086227Stmm	flags &= ~RF_ACTIVE;
32186227Stmm
32286227Stmm	switch (type) {
32386227Stmm	case SYS_RES_IRQ:
32488823Stmm		rm = &sc->sc_intr_rman;
32586227Stmm		break;
32688823Stmm	case SYS_RES_MEMORY:
32788823Stmm		rm = &sc->sc_mem_rman;
32888823Stmm		break;
32986227Stmm	default:
33086227Stmm		return (NULL);
33186227Stmm	}
33286227Stmm
33386227Stmm	rv = rman_reserve_resource(rm, start, end, count, flags, child);
33486227Stmm	if (rv == NULL)
33586227Stmm		return (NULL);
33688823Stmm	if (type == SYS_RES_MEMORY) {
33788823Stmm		rman_set_bustag(rv, &nexus_bustag);
33888823Stmm		rman_set_bushandle(rv, rman_get_start(rv));
33988823Stmm	}
34086227Stmm
34186227Stmm	if (needactivate) {
342128776Stmm		if (bus_activate_resource(child, type, *rid, rv) != 0) {
34386227Stmm			rman_release_resource(rv);
34486227Stmm			return (NULL);
34586227Stmm		}
34686227Stmm	}
34788823Stmm
34886227Stmm	return (rv);
34986227Stmm}
35086227Stmm
35186227Stmmstatic int
35286227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid,
35386227Stmm    struct resource *r)
35486227Stmm{
35586227Stmm
35686227Stmm	/* Not much to be done yet... */
35786227Stmm	return (rman_activate_resource(r));
35886227Stmm}
35986227Stmm
36086227Stmmstatic int
36186227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
36286227Stmm    struct resource *r)
36386227Stmm{
36488823Stmm
36586227Stmm	/* Not much to be done yet... */
36686227Stmm	return (rman_deactivate_resource(r));
36786227Stmm}
36886227Stmm
36986227Stmmstatic int
37086227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid,
37186227Stmm		       struct resource *r)
37286227Stmm{
37386227Stmm	int error;
37486227Stmm
37586227Stmm	if (rman_get_flags(r) & RF_ACTIVE) {
37686227Stmm		error = bus_deactivate_resource(child, type, rid, r);
37786227Stmm		if (error)
378128776Stmm			return (error);
37986227Stmm	}
38086227Stmm	return (rman_release_resource(r));
38186227Stmm}
382