nexus.c revision 157896
1139825Simp/*-
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
34146474Smarius#include <sys/cdefs.h>
35146474Smarius__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/nexus.c 157896 2006-04-20 04:20:41Z imp $");
36146474Smarius
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
59133862Smarius * hang from the Open Firmware 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
67133862Smarius * work for all Open Firmware 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;
91146474Smariusstatic bus_add_child_t nexus_add_child;
92128776Stmmstatic bus_probe_nomatch_t nexus_probe_nomatch;
93128776Stmmstatic bus_read_ivar_t nexus_read_ivar;
94128776Stmmstatic bus_setup_intr_t nexus_setup_intr;
95128776Stmmstatic bus_teardown_intr_t nexus_teardown_intr;
96128776Stmmstatic bus_alloc_resource_t nexus_alloc_resource;
97128776Stmmstatic bus_activate_resource_t nexus_activate_resource;
98128776Stmmstatic bus_deactivate_resource_t nexus_deactivate_resource;
99128776Stmmstatic bus_release_resource_t nexus_release_resource;
10086227Stmm
10186227Stmmstatic device_method_t nexus_methods[] = {
10286227Stmm	/* Device interface */
10386227Stmm	DEVMETHOD(device_probe,		nexus_probe),
104128776Stmm	DEVMETHOD(device_attach,	nexus_attach),
10586227Stmm	DEVMETHOD(device_detach,	bus_generic_detach),
10686227Stmm	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10786227Stmm	DEVMETHOD(device_suspend,	bus_generic_suspend),
10886227Stmm	DEVMETHOD(device_resume,	bus_generic_resume),
10986227Stmm
110128776Stmm	/* Bus interface. */
111146474Smarius	DEVMETHOD(bus_add_child,	nexus_add_child),
11286227Stmm	DEVMETHOD(bus_print_child,	bus_generic_print_child),
11386227Stmm	DEVMETHOD(bus_probe_nomatch,	nexus_probe_nomatch),
11486227Stmm	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
11586227Stmm	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
11686227Stmm	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
11786227Stmm	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
11886227Stmm	DEVMETHOD(bus_activate_resource,	nexus_activate_resource),
11986227Stmm	DEVMETHOD(bus_deactivate_resource,	nexus_deactivate_resource),
12086227Stmm	DEVMETHOD(bus_release_resource,	nexus_release_resource),
12186227Stmm
12286227Stmm	{ 0, 0 }
12386227Stmm};
12486227Stmm
12586227Stmmstatic driver_t nexus_driver = {
12686227Stmm	"nexus",
12786227Stmm	nexus_methods,
12888823Stmm	sizeof(struct nexus_softc),
12986227Stmm};
13086227Stmm
13186227Stmmstatic devclass_t nexus_devclass;
13286227Stmm
13386227StmmDRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
13486227Stmm
13586227Stmmstatic char *nexus_excl_name[] = {
13690622Stmm	"aliases",
13790622Stmm	"chosen",
13890622Stmm	"counter-timer",	/* No separate device; handled by psycho/sbus */
13986227Stmm	"memory",
14090622Stmm	"openprom",
14186227Stmm	"options",
14286227Stmm	"packages",
14390622Stmm	"virtual-memory",
14486227Stmm	NULL
14586227Stmm};
14686227Stmm
14786227Stmmstatic char *nexus_excl_type[] = {
14886227Stmm	"cpu",
14986227Stmm	NULL
15086227Stmm};
15186227Stmm
15286227Stmmextern struct bus_space_tag nexus_bustag;
15386227Stmmextern struct bus_dma_tag nexus_dmatag;
15486227Stmm
15586227Stmmstatic int
15686227Stmmnexus_inlist(char *name, char *list[])
15786227Stmm{
15886227Stmm	int i;
15986227Stmm
16086227Stmm	for (i = 0; list[i] != NULL; i++)
16186227Stmm		if (strcmp(name, list[i]) == 0)
16286227Stmm			return (1);
16386227Stmm	return (0);
16486227Stmm}
16586227Stmm
16686227Stmm#define	NEXUS_EXCLUDED(name, type)					\
16786227Stmm	(nexus_inlist((name), nexus_excl_name) ||			\
16886227Stmm	((type) != NULL && nexus_inlist((type), nexus_excl_type)))
16986227Stmm
17086227Stmmstatic int
17186227Stmmnexus_probe(device_t dev)
17286227Stmm{
173128776Stmm
174128776Stmm	/* Nexus does always match. */
175133862Smarius	device_set_desc(dev, "Open Firmware Nexus device");
176128776Stmm	return (0);
177128776Stmm}
178128776Stmm
179128776Stmmstatic int
180128776Stmmnexus_attach(device_t dev)
181128776Stmm{
18286227Stmm	phandle_t root;
18386227Stmm	phandle_t child;
18486227Stmm	device_t cdev;
18586227Stmm	struct nexus_devinfo *dinfo;
18688823Stmm	struct nexus_softc *sc;
18786227Stmm	char *name, *type;
18886227Stmm
18986227Stmm	if ((root = OF_peer(0)) == -1)
19086227Stmm		panic("nexus_probe: OF_peer failed.");
19186227Stmm
19288823Stmm	sc = device_get_softc(dev);
19388823Stmm	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
19488823Stmm	sc->sc_intr_rman.rm_descr = "Interrupts";
19588823Stmm	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
19688823Stmm	sc->sc_mem_rman.rm_descr = "UPA Device Memory";
19788823Stmm	if (rman_init(&sc->sc_intr_rman) != 0 ||
19888823Stmm	    rman_init(&sc->sc_mem_rman) != 0 ||
19997265Sjake	    rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 ||
20088823Stmm	    rman_manage_region(&sc->sc_mem_rman, UPA_MEMSTART, UPA_MEMEND) != 0)
201128776Stmm		panic("nexus_attach(): failed to set up rmans");
202146474Smarius
203146474Smarius	/*
204146474Smarius	 * Allow devices to identify.
205146474Smarius	 */
206146474Smarius	bus_generic_probe(dev);
207146474Smarius
208146474Smarius	/*
209146474Smarius	 * Now walk the OFW tree and attach top-level devices.
210146474Smarius	 */
21186227Stmm	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
21286227Stmm		if (child == -1)
213128776Stmm			panic("nexus_attach(): OF_child() failed.");
21486227Stmm		if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1)
21586227Stmm			continue;
21686227Stmm		OF_getprop_alloc(child, "device_type", 1, (void **)&type);
21786227Stmm		if (NEXUS_EXCLUDED(name, type)) {
21886227Stmm			free(name, M_OFWPROP);
219128776Stmm			free(type, M_OFWPROP);
22086227Stmm			continue;
22188823Stmm		}
22286227Stmm		cdev = device_add_child(dev, NULL, -1);
223128776Stmm		if (cdev == NULL)
224128776Stmm			panic("nexus_attach(): device_add_child() failed.");
225128776Stmm		dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
226128776Stmm		dinfo->ndi_node = child;
227128776Stmm		dinfo->ndi_name = name;
228128776Stmm		dinfo->ndi_device_type = type;
229128776Stmm		OF_getprop_alloc(child, "model", 1,
230128776Stmm		    (void **)&dinfo->ndi_model);
231128776Stmm		dinfo->ndi_nreg = OF_getprop_alloc(child, "reg",
232128776Stmm		    sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg);
233128776Stmm		dinfo->ndi_ninterrupts = OF_getprop_alloc(child,
234128776Stmm		    "interrupts", sizeof(*dinfo->ndi_interrupts),
235128776Stmm		    (void **)&dinfo->ndi_interrupts);
236128776Stmm		device_set_ivars(cdev, dinfo);
23786227Stmm	}
238128776Stmm	return (bus_generic_attach(dev));
23986227Stmm}
24086227Stmm
241146474Smariusstatic device_t
242146474Smariusnexus_add_child(device_t dev, int order, const char *name, int unit)
243146474Smarius{
244146474Smarius	device_t cdev;
245146474Smarius	struct nexus_devinfo *dinfo;
246146474Smarius
247146474Smarius	cdev = device_add_child_ordered(dev, order, name, unit);
248146474Smarius	if (cdev == NULL)
249146474Smarius		return (NULL);
250146474Smarius
251146474Smarius	dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_NOWAIT | M_ZERO);
252146474Smarius	if (dinfo == NULL)
253146474Smarius		return (NULL);
254146474Smarius
255146474Smarius	dinfo->ndi_node = -1;
256146474Smarius	dinfo->ndi_name = strdup(name, M_OFWPROP);
257146474Smarius	device_set_ivars(cdev, dinfo);
258146474Smarius
259146474Smarius	return (cdev);
260146474Smarius}
261146474Smarius
26286227Stmmstatic void
26386227Stmmnexus_probe_nomatch(device_t dev, device_t child)
26486227Stmm{
26586227Stmm	char *type;
26686227Stmm
267128939Smarius	if ((type = nexus_get_device_type(child)) == NULL)
26886227Stmm		type = "(unknown)";
26986227Stmm	device_printf(dev, "<%s>, type %s (no driver attached)\n",
270128939Smarius	    nexus_get_name(child), type);
27186227Stmm}
27286227Stmm
27386227Stmmstatic int
27486227Stmmnexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
27586227Stmm{
27686227Stmm	struct nexus_devinfo *dinfo;
27786227Stmm
27886227Stmm	if ((dinfo = device_get_ivars(child)) == 0)
27986227Stmm		return (ENOENT);
28086227Stmm	switch (which) {
28186227Stmm	case NEXUS_IVAR_NODE:
28286227Stmm		*result = dinfo->ndi_node;
28386227Stmm		break;
28486227Stmm	case NEXUS_IVAR_NAME:
28586227Stmm		*result = (uintptr_t)dinfo->ndi_name;
28686227Stmm		break;
28786227Stmm	case NEXUS_IVAR_DEVICE_TYPE:
28886227Stmm		*result = (uintptr_t)dinfo->ndi_device_type;
28986227Stmm		break;
29086227Stmm	case NEXUS_IVAR_MODEL:
29186227Stmm		*result = (uintptr_t)dinfo->ndi_model;
29286227Stmm		break;
29386227Stmm	case NEXUS_IVAR_REG:
29486227Stmm		*result = (uintptr_t)dinfo->ndi_reg;
29586227Stmm		break;
29686227Stmm	case NEXUS_IVAR_NREG:
29786227Stmm		*result = dinfo->ndi_nreg;
29886227Stmm		break;
29986227Stmm	case NEXUS_IVAR_INTERRUPTS:
30086227Stmm		*result = (uintptr_t)dinfo->ndi_interrupts;
30186227Stmm		break;
30286227Stmm	case NEXUS_IVAR_NINTERRUPTS:
30386227Stmm		*result = dinfo->ndi_ninterrupts;
30486227Stmm		break;
30586227Stmm	case NEXUS_IVAR_DMATAG:
306128776Stmm		*result = (uintptr_t)&nexus_dmatag;
30786227Stmm		break;
30886227Stmm	default:
30986227Stmm		return (ENOENT);
31086227Stmm	}
31186227Stmm	return 0;
31286227Stmm}
31386227Stmm
31486227Stmmstatic int
31586227Stmmnexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
31686227Stmm    driver_intr_t *intr, void *arg, void **cookiep)
31786227Stmm{
31886227Stmm	int error;
31988823Stmm
32086227Stmm	if (res == NULL)
32186227Stmm		panic("nexus_setup_intr: NULL interrupt resource!");
32286227Stmm
323131535Simp	if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
32486227Stmm		flags |= INTR_EXCL;
32586227Stmm
326128776Stmm	/* We depend here on rman_activate_resource() being idempotent. */
32786227Stmm	error = rman_activate_resource(res);
32886227Stmm	if (error)
32986227Stmm		return (error);
33086227Stmm
331131535Simp	error = inthand_add(device_get_nameunit(child), rman_get_start(res),
33286227Stmm	    intr, arg, flags, cookiep);
33386227Stmm
33486227Stmm	return (error);
33586227Stmm}
33686227Stmm
33786227Stmmstatic int
33886227Stmmnexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
33986227Stmm{
340146474Smarius
341131535Simp	inthand_remove(rman_get_start(r), ih);
34286227Stmm	return (0);
34386227Stmm}
34486227Stmm
34586227Stmmstatic struct resource *
34686227Stmmnexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
34786227Stmm    u_long start, u_long end, u_long count, u_int flags)
34886227Stmm{
34988823Stmm	struct	nexus_softc *sc = device_get_softc(bus);
35086227Stmm	struct	resource *rv;
35186227Stmm	struct	rman *rm;
35286227Stmm	int needactivate = flags & RF_ACTIVE;
35386227Stmm
35486227Stmm	flags &= ~RF_ACTIVE;
35586227Stmm
35686227Stmm	switch (type) {
35786227Stmm	case SYS_RES_IRQ:
35888823Stmm		rm = &sc->sc_intr_rman;
35986227Stmm		break;
36088823Stmm	case SYS_RES_MEMORY:
36188823Stmm		rm = &sc->sc_mem_rman;
36288823Stmm		break;
36386227Stmm	default:
36486227Stmm		return (NULL);
36586227Stmm	}
36686227Stmm
36786227Stmm	rv = rman_reserve_resource(rm, start, end, count, flags, child);
36886227Stmm	if (rv == NULL)
36986227Stmm		return (NULL);
370157896Simp	rman_set_rid(rv, *rid);
37188823Stmm	if (type == SYS_RES_MEMORY) {
37288823Stmm		rman_set_bustag(rv, &nexus_bustag);
37388823Stmm		rman_set_bushandle(rv, rman_get_start(rv));
37488823Stmm	}
37586227Stmm
37686227Stmm	if (needactivate) {
377128776Stmm		if (bus_activate_resource(child, type, *rid, rv) != 0) {
37886227Stmm			rman_release_resource(rv);
37986227Stmm			return (NULL);
38086227Stmm		}
38186227Stmm	}
38288823Stmm
38386227Stmm	return (rv);
38486227Stmm}
38586227Stmm
38686227Stmmstatic int
38786227Stmmnexus_activate_resource(device_t bus, device_t child, int type, int rid,
38886227Stmm    struct resource *r)
38986227Stmm{
39086227Stmm
39186227Stmm	/* Not much to be done yet... */
39286227Stmm	return (rman_activate_resource(r));
39386227Stmm}
39486227Stmm
39586227Stmmstatic int
39686227Stmmnexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
39786227Stmm    struct resource *r)
39886227Stmm{
39988823Stmm
40086227Stmm	/* Not much to be done yet... */
40186227Stmm	return (rman_deactivate_resource(r));
40286227Stmm}
40386227Stmm
40486227Stmmstatic int
40586227Stmmnexus_release_resource(device_t bus, device_t child, int type, int rid,
40686227Stmm		       struct resource *r)
40786227Stmm{
40886227Stmm	int error;
40986227Stmm
41086227Stmm	if (rman_get_flags(r) & RF_ACTIVE) {
41186227Stmm		error = bus_deactivate_resource(child, type, rid, r);
41286227Stmm		if (error)
413128776Stmm			return (error);
41486227Stmm	}
41586227Stmm	return (rman_release_resource(r));
41686227Stmm}
417