nexus.c revision 128939
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 128939 2004-05-04 22:31:16Z marius $
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>
4286227Stmm
4386227Stmm#include <dev/ofw/openfirm.h>
4486227Stmm
4586227Stmm#include <machine/bus.h>
4686227Stmm#include <machine/frame.h>
4786227Stmm#include <machine/intr_machdep.h>
4886227Stmm#include <machine/nexusvar.h>
4988823Stmm#include <machine/ofw_upa.h>
5086227Stmm#include <machine/resource.h>
5188823Stmm#include <machine/upa.h>
5286227Stmm
5386227Stmm#include <sys/rman.h>
5486227Stmm
5586227Stmm/*
5686227Stmm * The nexus (which is a pseudo-bus actually) iterates over the nodes that
57128776Stmm * hang from the OpenFirmware root node and adds them as devices to this bus
5886227Stmm * (except some special nodes which are excluded) so that drivers can be
59128776Stmm * attached to them.
6086227Stmm *
6186227Stmm * Additionally, interrupt setup/teardown and some resource management are
6286227Stmm * done at this level.
6386227Stmm *
6486227Stmm * Maybe this code should get into dev/ofw to some extent, as some of it should
6586227Stmm * work for all OpenFirmware based machines...
6686227Stmm */
6786227Stmm
6886227Stmmstatic MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
6986227Stmm
7086227Stmmstruct nexus_devinfo {
7186227Stmm	phandle_t	ndi_node;
7286227Stmm	/* Some common properties. */
7386227Stmm	char		*ndi_name;
7486227Stmm	char		*ndi_device_type;
7586227Stmm	char		*ndi_model;
7688823Stmm	struct		upa_regs *ndi_reg;
7786227Stmm	int		ndi_nreg;
7886227Stmm	u_int		*ndi_interrupts;
7986227Stmm	int		ndi_ninterrupts;
8086227Stmm};
8186227Stmm
8288823Stmmstruct nexus_softc {
8388823Stmm	struct rman	sc_intr_rman;
8488823Stmm	struct rman	sc_mem_rman;
8588823Stmm};
8688823Stmm
87128776Stmmstatic device_probe_t nexus_probe;
88128776Stmmstatic device_attach_t nexus_attach;
89128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch;
90128776Stmmstatic bus_read_ivar_t nexus_read_ivar;
91128776Stmmstatic bus_setup_intr_t nexus_setup_intr;
92128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr;
93128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource;
94128776Stmmstatic bus_activate_resource_t nexus_activate_resource;
95128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource;
96128776Stmmstatic bus_release_resource_t nexus_release_resource;
9786227Stmm
9886227Stmmstatic device_method_t nexus_methods[] = {
9986227Stmm	/* Device interface */
10086227Stmm	DEVMETHOD(device_probe,		nexus_probe),
101128776Stmm	DEVMETHOD(device_attach,	nexus_attach),
10286227Stmm	DEVMETHOD(device_detach,	bus_generic_detach),
10386227Stmm	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10486227Stmm	DEVMETHOD(device_suspend,	bus_generic_suspend),
10586227Stmm	DEVMETHOD(device_resume,	bus_generic_resume),
10686227Stmm
107128776Stmm	/* Bus interface. */
10886227Stmm	DEVMETHOD(bus_print_child,	bus_generic_print_child),
10986227Stmm	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
11086227Stmm	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
11186227Stmm	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
11286227Stmm	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
11386227Stmm	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
11486227Stmm	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
11586227Stmm	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
11686227Stmm	DEVMETHOD(bus_release_resource,	nexus_release_resource),
11786227Stmm
11886227Stmm	{ 0, 0 }
11986227Stmm};
12086227Stmm
12186227Stmmstatic driver_t nexus_driver = {
12286227Stmm	"nexus",
12386227Stmm	nexus_methods,
12488823Stmm	sizeof(struct nexus_softc),
12586227Stmm};
12686227Stmm
12786227Stmmstatic devclass_t nexus_devclass;
12886227Stmm
12986227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
13086227Stmm
13186227Stmmstatic char *nexus_excl_name[] = {
13290622Stmm	"aliases",
13390622Stmm	"chosen",
13490622Stmm	"counter-timer",	/* No separate device; handled by psycho/sbus */
13586227Stmm	"memory",
13690622Stmm	"openprom",
13786227Stmm	"options",
13886227Stmm	"packages",
13990622Stmm	"virtual-memory",
14086227Stmm	NULL
14186227Stmm};
14286227Stmm
14386227Stmmstatic char *nexus_excl_type[] = {
14486227Stmm	"cpu",
14586227Stmm	NULL
14686227Stmm};
14786227Stmm
14886227Stmmextern struct bus_space_tag nexus_bustag;
14986227Stmmextern struct bus_dma_tag nexus_dmatag;
15086227Stmm
15186227Stmmstatic int
15286227Stmmnexus_inlist(char *name, char *list[])
15386227Stmm{
15486227Stmm	int i;
15586227Stmm
15686227Stmm	for (i = 0; list[i] != NULL; i++)
15786227Stmm		if (strcmp(name, list[i]) == 0)
15886227Stmm			return (1);
15986227Stmm	return (0);
16086227Stmm}
16186227Stmm
16286227Stmm#define	NEXUS_EXCLUDED(name, type)					\
16386227Stmm	(nexus_inlist((name), nexus_excl_name) ||			\
16486227Stmm	((type) != NULL && nexus_inlist((type), nexus_excl_type)))
16586227Stmm
16686227Stmmstatic int
16786227Stmmnexus_probe(device_t dev)
16886227Stmm{
169128776Stmm
170128776Stmm	/* Nexus does always match. */
171128776Stmm	device_set_desc(dev, "OpenFirmware Nexus device");
172128776Stmm	return (0);
173128776Stmm}
174128776Stmm
175128776Stmmstatic int
176128776Stmmnexus_attach(device_t dev)
177128776Stmm{
17886227Stmm	phandle_t root;
17986227Stmm	phandle_t child;
18086227Stmm	device_t cdev;
18186227Stmm	struct nexus_devinfo *dinfo;
18288823Stmm	struct nexus_softc *sc;
18386227Stmm	char *name, *type;
18486227Stmm
18586227Stmm	if ((root = OF_peer(0)) == -1)
18686227Stmm		panic("nexus_probe: OF_peer failed.");
18786227Stmm
18888823Stmm	sc = device_get_softc(dev);
18988823Stmm	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
19088823Stmm	sc->sc_intr_rman.rm_descr = "Interrupts";
19188823Stmm	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
19288823Stmm	sc->sc_mem_rman.rm_descr = "UPA Device Memory";
19388823Stmm	if (rman_init(&sc->sc_intr_rman) != 0 ||
19488823Stmm	    rman_init(&sc->sc_mem_rman) != 0 ||
19597265Sjake	    rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 ||
19688823Stmm	    rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0)
197128776Stmm		panic("nexus_attach(): failed to set up rmans");
19886227Stmm	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
19986227Stmm		if (child == -1)
200128776Stmm			panic("nexus_attach(): OF_child() failed.");
20186227Stmm		if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1)
20286227Stmm			continue;
20386227Stmm		OF_getprop_alloc(child, "device_type", 1, (void **)&type);
20486227Stmm		if (NEXUS_EXCLUDED(name, type)) {
20586227Stmm			free(name, M_OFWPROP);
206128776Stmm			free(type, M_OFWPROP);
20786227Stmm			continue;
20888823Stmm		}
20986227Stmm		cdev = device_add_child(dev, NULL, -1);
210128776Stmm		if (cdev == NULL)
211128776Stmm			panic("nexus_attach(): device_add_child() failed.");
212128776Stmm		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
213128776Stmm		dinfo->ndi_node = child;
214128776Stmm		dinfo->ndi_name = name;
215128776Stmm		dinfo->ndi_device_type = type;
216128776Stmm		OF_getprop_alloc(child, "model", 1,
217128776Stmm		    (void **)&dinfo->ndi_model);
218128776Stmm		dinfo->ndi_nreg = OF_getprop_alloc(child, "reg",
219128776Stmm		    sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg);
220128776Stmm		dinfo->ndi_ninterrupts = OF_getprop_alloc(child,
221128776Stmm		    "interrupts", sizeof(*dinfo->ndi_interrupts),
222128776Stmm		    (void **)&dinfo->ndi_interrupts);
223128776Stmm		device_set_ivars(cdev, dinfo);
22486227Stmm	}
225128776Stmm	return (bus_generic_attach(dev));
22686227Stmm}
22786227Stmm
22886227Stmmstatic void
22986227Stmmnexus_probe_nomatch(device_t dev, device_t child)
23086227Stmm{
23186227Stmm	char *type;
23286227Stmm
233128939Smarius	if ((type = nexus_get_device_type(child)) == NULL)
23486227Stmm		type = "(unknown)";
23586227Stmm	device_printf(dev, "<%s>, type %s (no driver attached)\n",
236128939Smarius	    nexus_get_name(child), type);
23786227Stmm}
23886227Stmm
23986227Stmmstatic int
24086227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
24186227Stmm{
24286227Stmm	struct nexus_devinfo *dinfo;
24386227Stmm
24486227Stmm	if ((dinfo = device_get_ivars(child)) == 0)
24586227Stmm		return (ENOENT);
24686227Stmm	switch (which) {
24786227Stmm	case NEXUS_IVAR_NODE:
24886227Stmm		*result = dinfo->ndi_node;
24986227Stmm		break;
25086227Stmm	case NEXUS_IVAR_NAME:
25186227Stmm		*result = (uintptr_t)dinfo->ndi_name;
25286227Stmm		break;
25386227Stmm	case NEXUS_IVAR_DEVICE_TYPE:
25486227Stmm		*result = (uintptr_t)dinfo->ndi_device_type;
25586227Stmm		break;
25686227Stmm	case NEXUS_IVAR_MODEL:
25786227Stmm		*result = (uintptr_t)dinfo->ndi_model;
25886227Stmm		break;
25986227Stmm	case NEXUS_IVAR_REG:
26086227Stmm		*result = (uintptr_t)dinfo->ndi_reg;
26186227Stmm		break;
26286227Stmm	case NEXUS_IVAR_NREG:
26386227Stmm		*result = dinfo->ndi_nreg;
26486227Stmm		break;
26586227Stmm	case NEXUS_IVAR_INTERRUPTS:
26686227Stmm		*result = (uintptr_t)dinfo->ndi_interrupts;
26786227Stmm		break;
26886227Stmm	case NEXUS_IVAR_NINTERRUPTS:
26986227Stmm		*result = dinfo->ndi_ninterrupts;
27086227Stmm		break;
27186227Stmm	case NEXUS_IVAR_DMATAG:
272128776Stmm		*result = (uintptr_t)&nexus_dmatag;
27386227Stmm		break;
27486227Stmm	default:
27586227Stmm		return (ENOENT);
27686227Stmm	}
27786227Stmm	return 0;
27886227Stmm}
27986227Stmm
28086227Stmmstatic int
28186227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
28286227Stmm    driver_intr_t *intr, void *arg, void **cookiep)
28386227Stmm{
28486227Stmm	int error;
28588823Stmm
28686227Stmm	if (res == NULL)
28786227Stmm		panic("nexus_setup_intr: NULL interrupt resource!");
28886227Stmm
28986227Stmm	if ((res->r_flags & RF_SHAREABLE) == 0)
29086227Stmm		flags |= INTR_EXCL;
29186227Stmm
292128776Stmm	/* We depend here on rman_activate_resource() being idempotent. */
29386227Stmm	error = rman_activate_resource(res);
29486227Stmm	if (error)
29586227Stmm		return (error);
29686227Stmm
29786227Stmm	error = inthand_add(device_get_nameunit(child), res->r_start,
29886227Stmm	    intr, arg, flags, cookiep);
29986227Stmm
30086227Stmm	return (error);
30186227Stmm}
30286227Stmm
30386227Stmmstatic int
30486227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
30586227Stmm{
30686227Stmm	inthand_remove(r->r_start, ih);
30786227Stmm	return (0);
30886227Stmm}
30986227Stmm
31086227Stmmstatic struct resource *
31186227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
31286227Stmm    u_long start, u_long end, u_long count, u_int flags)
31386227Stmm{
31488823Stmm	struct	nexus_softc *sc = device_get_softc(bus);
31586227Stmm	struct	resource *rv;
31686227Stmm	struct	rman *rm;
31786227Stmm	int needactivate = flags & RF_ACTIVE;
31886227Stmm
31986227Stmm	flags &= ~RF_ACTIVE;
32086227Stmm
32186227Stmm	switch (type) {
32286227Stmm	case SYS_RES_IRQ:
32388823Stmm		rm = &sc->sc_intr_rman;
32486227Stmm		break;
32588823Stmm	case SYS_RES_MEMORY:
32688823Stmm		rm = &sc->sc_mem_rman;
32788823Stmm		break;
32886227Stmm	default:
32986227Stmm		return (NULL);
33086227Stmm	}
33186227Stmm
33286227Stmm	rv = rman_reserve_resource(rm, start, end, count, flags, child);
33386227Stmm	if (rv == NULL)
33486227Stmm		return (NULL);
33588823Stmm	if (type == SYS_RES_MEMORY) {
33688823Stmm		rman_set_bustag(rv, &nexus_bustag);
33788823Stmm		rman_set_bushandle(rv, rman_get_start(rv));
33888823Stmm	}
33986227Stmm
34086227Stmm	if (needactivate) {
341128776Stmm		if (bus_activate_resource(child, type, *rid, rv) != 0) {
34286227Stmm			rman_release_resource(rv);
34386227Stmm			return (NULL);
34486227Stmm		}
34586227Stmm	}
34688823Stmm
34786227Stmm	return (rv);
34886227Stmm}
34986227Stmm
35086227Stmmstatic int
35186227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid,
35286227Stmm    struct resource *r)
35386227Stmm{
35486227Stmm
35586227Stmm	/* Not much to be done yet... */
35686227Stmm	return (rman_activate_resource(r));
35786227Stmm}
35886227Stmm
35986227Stmmstatic int
36086227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
36186227Stmm    struct resource *r)
36286227Stmm{
36388823Stmm
36486227Stmm	/* Not much to be done yet... */
36586227Stmm	return (rman_deactivate_resource(r));
36686227Stmm}
36786227Stmm
36886227Stmmstatic int
36986227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid,
37086227Stmm		       struct resource *r)
37186227Stmm{
37286227Stmm	int error;
37386227Stmm
37486227Stmm	if (rman_get_flags(r) & RF_ACTIVE) {
37586227Stmm		error = bus_deactivate_resource(child, type, rid, r);
37686227Stmm		if (error)
377128776Stmm			return (error);
37886227Stmm	}
37986227Stmm	return (rman_release_resource(r));
38086227Stmm}
381