summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authortmm <tmm@FreeBSD.org>2001-11-09 20:01:25 +0000
committertmm <tmm@FreeBSD.org>2001-11-09 20:01:25 +0000
commit8f24e75043dc5e79a13e508a4218d9a16562d87f (patch)
tree7ae16de8a2a3dec3823f924f7c5b3066c4d87161 /sys
parent30ac245755df3e0b5cca17ce6a1ab2ee59d9c29b (diff)
downloadFreeBSD-src-8f24e75043dc5e79a13e508a4218d9a16562d87f.zip
FreeBSD-src-8f24e75043dc5e79a13e508a4218d9a16562d87f.tar.gz
Add a nexus device for sparc64, which uses the OpenFirmware to attach UPA
devices (mostly host bridges) and handles interrupt allocation and setup.
Diffstat (limited to 'sys')
-rw-r--r--sys/sparc64/include/nexusvar.h71
-rw-r--r--sys/sparc64/sparc64/autoconf.c17
-rw-r--r--sys/sparc64/sparc64/nexus.c434
3 files changed, 521 insertions, 1 deletions
diff --git a/sys/sparc64/include/nexusvar.h b/sys/sparc64/include/nexusvar.h
new file mode 100644
index 0000000..5ad396b
--- /dev/null
+++ b/sys/sparc64/include/nexusvar.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACHINE_NEXUSVAR_H_
+#define _MACHINE_NEXUSVAR_H_
+
+enum nexus_ivars {
+ NEXUS_IVAR_NODE,
+ NEXUS_IVAR_NAME,
+ NEXUS_IVAR_DEVICE_TYPE,
+ NEXUS_IVAR_MODEL,
+ NEXUS_IVAR_REG,
+ NEXUS_IVAR_NREG,
+ NEXUS_IVAR_INTERRUPTS,
+ NEXUS_IVAR_NINTERRUPTS,
+ NEXUS_IVAR_BUSTAG,
+ NEXUS_IVAR_DMATAG,
+};
+
+/* XXX: these are the UPA registers and should probably go elsewhere */
+struct ofw_nexus_reg {
+ int64_t or_paddr;
+ int64_t or_len;
+};
+
+/*
+ * Simplified accessors for nexus devices
+ * XXX: These should be made specializations of generic bus accessor macros
+ * instead of having multiple implementations around.
+ */
+#define NEXUS_ACCESSOR(var, ivar, type) \
+ __BUS_ACCESSOR(nexus, var, NEXUS, ivar, type)
+
+NEXUS_ACCESSOR(node, NODE, phandle_t)
+NEXUS_ACCESSOR(name, NAME, char *)
+NEXUS_ACCESSOR(device_type, DEVICE_TYPE, char *)
+NEXUS_ACCESSOR(model, MODEL, char *)
+NEXUS_ACCESSOR(reg, REG, struct ofw_nexus_reg *)
+NEXUS_ACCESSOR(nreg, NREG, int)
+NEXUS_ACCESSOR(interrupts, INTERRUPTS, u_int *)
+NEXUS_ACCESSOR(ninterrupts, NINTERRUPTS, int)
+NEXUS_ACCESSOR(bustag, BUSTAG, bus_space_tag_t)
+NEXUS_ACCESSOR(dmatag, DMATAG, bus_dma_tag_t)
+
+#undef NEXUS_ACCESSOR
+
+#endif /* _MACHINE_NEXUSVAR_H_ */
diff --git a/sys/sparc64/sparc64/autoconf.c b/sys/sparc64/sparc64/autoconf.c
index 35b1923..730e922 100644
--- a/sys/sparc64/sparc64/autoconf.c
+++ b/sys/sparc64/sparc64/autoconf.c
@@ -26,12 +26,19 @@
* $FreeBSD$
*/
+#include "opt_isa.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cons.h>
#include <sys/kernel.h>
+#ifdef DEV_ISA
+#include <isa/isavar.h>
+extern device_t isa_bus_device;
+#endif
+
dev_t dumpdev = NODEV;
dev_t rootdev = NODEV;
@@ -39,10 +46,18 @@ static void configure(void *);
SYSINIT(configure, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure, NULL);
+static device_t nexusdev;
+
+
static void
configure(void *v)
{
- device_add_child(root_bus, "upa", 0);
+
+ nexusdev = device_add_child(root_bus, "nexus", 0);
root_bus_configure();
+#ifdef DEV_ISA
+ if (isa_bus_device != NULL)
+ isa_probe_children(isa_bus_device);
+#endif
cold = 0;
}
diff --git a/sys/sparc64/sparc64/nexus.c b/sys/sparc64/sparc64/nexus.c
new file mode 100644
index 0000000..6fd462b
--- /dev/null
+++ b/sys/sparc64/sparc64/nexus.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright 1998 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cons.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+#include <machine/frame.h>
+#include <machine/intr_machdep.h>
+#include <machine/nexusvar.h>
+#include <machine/resource.h>
+
+#include <sys/rman.h>
+
+/*
+ * The nexus (which is a pseudo-bus actually) iterates over the nodes that
+ * hang from the OpenFirmware root node and add them as devices to this bus
+ * (except some special nodes which are excluded) so that drivers can be
+ * attached to them. This saves lots of detection work.
+ * Usually, all devices and bridges that are attached to the UltraSparc UPA
+ * bus will show up here, plus some pseudo-nodes which are excluded.
+ * For now, the only node that gets used is probably the pci bus one.
+ *
+ * Additionally, interrupt setup/teardown and some resource management are
+ * done at this level.
+ *
+ * Maybe this code should get into dev/ofw to some extent, as some of it should
+ * work for all OpenFirmware based machines...
+ */
+
+static MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
+
+struct nexus_devinfo {
+ phandle_t ndi_node;
+ /* Some common properties. */
+ char *ndi_name;
+ char *ndi_device_type;
+ char *ndi_model;
+ struct ofw_nexus_reg *ndi_reg;
+ int ndi_nreg;
+ u_int *ndi_interrupts;
+ int ndi_ninterrupts;
+ bus_space_tag_t ndi_bustag;
+ bus_dma_tag_t ndi_dmatag;
+};
+
+static int nexus_probe(device_t);
+static void nexus_probe_nomatch(device_t, device_t);
+static int nexus_read_ivar(device_t, device_t, int, uintptr_t *);
+static int nexus_write_ivar(device_t, device_t, int, uintptr_t);
+static int nexus_setup_intr(device_t, device_t, struct resource *, int,
+ driver_intr_t *, void *, void **);
+static int nexus_teardown_intr(device_t, device_t, struct resource *,
+ void *);
+static struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
+ u_long, u_long, u_long, u_int);
+static int nexus_activate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int nexus_deactivate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int nexus_release_resource(device_t, device_t, int, int,
+ struct resource *);
+
+static device_method_t nexus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, nexus_probe),
+ DEVMETHOD(device_attach, bus_generic_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface. Resource management is business of the children... */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch),
+ DEVMETHOD(bus_read_ivar, nexus_read_ivar),
+ DEVMETHOD(bus_write_ivar, nexus_write_ivar),
+ DEVMETHOD(bus_setup_intr, nexus_setup_intr),
+ DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, nexus_alloc_resource),
+ DEVMETHOD(bus_activate_resource, nexus_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
+ DEVMETHOD(bus_release_resource, nexus_release_resource),
+
+ { 0, 0 }
+};
+
+static driver_t nexus_driver = {
+ "nexus",
+ nexus_methods,
+ 1, /* no softc */
+};
+
+static devclass_t nexus_devclass;
+
+DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
+
+static char *nexus_excl_name[] = {
+ "virtual-memory",
+ "memory",
+ "aliases",
+ "options",
+ "openprom",
+ "chosen",
+ "packages",
+ NULL
+};
+
+static char *nexus_excl_type[] = {
+ "cpu",
+ NULL
+};
+
+struct rman intr_rman;
+extern struct bus_space_tag nexus_bustag;
+extern struct bus_dma_tag nexus_dmatag;
+
+static int
+nexus_inlist(char *name, char *list[])
+{
+ int i;
+
+ for (i = 0; list[i] != NULL; i++)
+ if (strcmp(name, list[i]) == 0)
+ return (1);
+ return (0);
+}
+
+#define NEXUS_EXCLUDED(name, type) \
+ (nexus_inlist((name), nexus_excl_name) || \
+ ((type) != NULL && nexus_inlist((type), nexus_excl_type)))
+
+static int
+nexus_probe(device_t dev)
+{
+ phandle_t root;
+ phandle_t child;
+ device_t cdev;
+ struct nexus_devinfo *dinfo;
+ char *name, *type;
+
+ if ((root = OF_peer(0)) == -1)
+ panic("nexus_probe: OF_peer failed.");
+
+ intr_rman.rm_type = RMAN_ARRAY;
+ intr_rman.rm_descr = "Interrupts";
+ if (rman_init(&intr_rman) != 0 ||
+ rman_manage_region(&intr_rman, 0, NIV - 1) != 0)
+ panic("nexus_probe: failed to set up rman");
+ for (child = OF_child(root); child != 0; child = OF_peer(child)) {
+ if (child == -1)
+ panic("nexus_probe(): OF_child failed.");
+ if (OF_getprop_alloc(child, "name", 1, (void **)&name) == -1)
+ continue;
+ OF_getprop_alloc(child, "device_type", 1, (void **)&type);
+ if (NEXUS_EXCLUDED(name, type)) {
+ free(name, M_OFWPROP);
+ if (type != NULL)
+ free(type, M_OFWPROP);
+ continue;
+ }
+ cdev = device_add_child(dev, NULL, -1);
+ if (cdev != NULL) {
+ dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
+ dinfo->ndi_node = child;
+ dinfo->ndi_name = name;
+ dinfo->ndi_device_type = type;
+ OF_getprop_alloc(child, "model", 1,
+ (void **)&dinfo->ndi_model);
+ dinfo->ndi_nreg = OF_getprop_alloc(child, "reg",
+ sizeof(*dinfo->ndi_reg), (void **)&dinfo->ndi_reg);
+ dinfo->ndi_ninterrupts = OF_getprop_alloc(child,
+ "interrupts", sizeof(*dinfo->ndi_interrupts),
+ (void **)&dinfo->ndi_interrupts);
+ dinfo->ndi_bustag = &nexus_bustag;
+ dinfo->ndi_dmatag = &nexus_dmatag;
+ device_set_ivars(cdev, dinfo);
+ } else
+ free(name, M_OFWPROP);
+
+ }
+ device_set_desc(dev, "OpenFirmware Nexus device");
+ return (0);
+}
+
+static void
+nexus_probe_nomatch(device_t dev, device_t child)
+{
+ char *name;
+ char *type;
+
+ if (BUS_READ_IVAR(dev, child, NEXUS_IVAR_NAME,
+ (uintptr_t *)&name) != 0 ||
+ BUS_READ_IVAR(dev, child, NEXUS_IVAR_DEVICE_TYPE,
+ (uintptr_t *)&type) != 0)
+ return;
+
+ if (type == NULL)
+ type = "(unknown)";
+ device_printf(dev, "<%s>, type %s (no driver attached)\n",
+ name, type);
+}
+
+static int
+nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct nexus_devinfo *dinfo;
+
+ if ((dinfo = device_get_ivars(child)) == 0)
+ return (ENOENT);
+ switch (which) {
+ case NEXUS_IVAR_NODE:
+ *result = dinfo->ndi_node;
+ break;
+ case NEXUS_IVAR_NAME:
+ *result = (uintptr_t)dinfo->ndi_name;
+ break;
+ case NEXUS_IVAR_DEVICE_TYPE:
+ *result = (uintptr_t)dinfo->ndi_device_type;
+ break;
+ case NEXUS_IVAR_MODEL:
+ *result = (uintptr_t)dinfo->ndi_model;
+ break;
+ case NEXUS_IVAR_REG:
+ *result = (uintptr_t)dinfo->ndi_reg;
+ break;
+ case NEXUS_IVAR_NREG:
+ *result = dinfo->ndi_nreg;
+ break;
+ case NEXUS_IVAR_INTERRUPTS:
+ *result = (uintptr_t)dinfo->ndi_interrupts;
+ break;
+ case NEXUS_IVAR_NINTERRUPTS:
+ *result = dinfo->ndi_ninterrupts;
+ break;
+ case NEXUS_IVAR_BUSTAG:
+ *result = (uintptr_t)dinfo->ndi_bustag;
+ break;
+ case NEXUS_IVAR_DMATAG:
+ *result = (uintptr_t)dinfo->ndi_dmatag;
+ break;
+ default:
+ return (ENOENT);
+ }
+ return 0;
+}
+
+static int
+nexus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ struct nexus_devinfo *dinfo;
+
+ if ((dinfo = device_get_ivars(child)) == 0)
+ return (ENOENT);
+
+ switch (which) {
+ case NEXUS_IVAR_NODE:
+ case NEXUS_IVAR_NAME:
+ case NEXUS_IVAR_DEVICE_TYPE:
+ case NEXUS_IVAR_MODEL:
+ case NEXUS_IVAR_REG:
+ case NEXUS_IVAR_NREG:
+ case NEXUS_IVAR_INTERRUPTS:
+ case NEXUS_IVAR_NINTERRUPTS:
+ case NEXUS_IVAR_BUSTAG:
+ case NEXUS_IVAR_DMATAG:
+ return (EINVAL);
+ default:
+ return (ENOENT);
+ }
+ return 0;
+}
+
+static int
+nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
+ driver_intr_t *intr, void *arg, void **cookiep)
+{
+ int error;
+
+ if (res == NULL)
+ panic("nexus_setup_intr: NULL interrupt resource!");
+
+ if ((res->r_flags & RF_SHAREABLE) == 0)
+ flags |= INTR_EXCL;
+
+ /*
+ * We depend here on rman_activate_resource() being idempotent.
+ */
+ error = rman_activate_resource(res);
+ if (error)
+ return (error);
+
+ error = inthand_add(device_get_nameunit(child), res->r_start,
+ intr, arg, flags, cookiep);
+
+ return (error);
+}
+
+static int
+nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
+{
+ inthand_remove(r->r_start, ih);
+ return (0);
+}
+
+/*
+ * Allocate resources at the behalf of a child. This only handles interrupts,
+ * since i/o resources are usually set up by the firmware, and thus need not
+ * be handled here.
+ */
+static struct resource *
+nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct resource *rv;
+ struct rman *rm;
+ int needactivate = flags & RF_ACTIVE;
+
+ flags &= ~RF_ACTIVE;
+
+ switch (type) {
+ case SYS_RES_IRQ:
+ rm = &intr_rman;
+ break;
+ default:
+ return (NULL);
+ }
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (rv == NULL)
+ return (NULL);
+ /* XXX: no bus_space_tag/bus_handle yet... */
+
+ if (needactivate) {
+ if (bus_activate_resource(child, type, *rid, rv)) {
+ rman_release_resource(rv);
+ return (NULL);
+ }
+ }
+
+ return (rv);
+}
+
+static int
+nexus_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+
+ /* Not much to be done yet... */
+ return (rman_activate_resource(r));
+}
+
+static int
+nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+
+ /* Not much to be done yet... */
+ return (rman_deactivate_resource(r));
+}
+
+static int
+nexus_release_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+ int error;
+
+ if (rman_get_flags(r) & RF_ACTIVE) {
+ error = bus_deactivate_resource(child, type, rid, r);
+ if (error)
+ return error;
+ }
+ return (rman_release_resource(r));
+}
OpenPOWER on IntegriCloud