summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortmm <tmm@FreeBSD.org>2001-11-09 20:19:58 +0000
committertmm <tmm@FreeBSD.org>2001-11-09 20:19:58 +0000
commitd9d3e67ce7bd0548d288122b0e6b2af477a6fc14 (patch)
tree8a0741ca1fda692aae84db81a64fc0b73bb5dfec
parent904109a4f9c52cc69f2df3836e4b3b9d4c2bf99d (diff)
downloadFreeBSD-src-d9d3e67ce7bd0548d288122b0e6b2af477a6fc14.zip
FreeBSD-src-d9d3e67ce7bd0548d288122b0e6b2af477a6fc14.tar.gz
Add support for the Sun psycho/sabre UPA-PCI bridge, some OpenFirmware
PCI support code, and a driver for the Sun APB PCI-PCI bridge. Partly ported from NetBSD.
-rw-r--r--sys/sparc64/pci/apb.c303
-rw-r--r--sys/sparc64/pci/ofw_pci.c267
-rw-r--r--sys/sparc64/pci/ofw_pci.h73
-rw-r--r--sys/sparc64/pci/psycho.c1210
-rw-r--r--sys/sparc64/pci/psychoreg.h437
-rw-r--r--sys/sparc64/pci/psychovar.h92
6 files changed, 2382 insertions, 0 deletions
diff --git a/sys/sparc64/pci/apb.c b/sys/sparc64/pci/apb.c
new file mode 100644
index 0000000..0667e20
--- /dev/null
+++ b/sys/sparc64/pci/apb.c
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
+ * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000 BSDi
+ * Copyright (c) 2001 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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/dev/pci/pci_pci.c,v 1.3 2000/12/13
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Support for the Sun APB (Advanced PCI Bridge) PCI-PCI bridge.
+ * This bridge does not fully comply to the PCI bridge specification, and is
+ * therefore not supported by the generic driver.
+ * We can use some pf the pcib methods anyway.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+
+#include <machine/resource.h>
+
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+#include <pci/pcib.h>
+
+#include "pcib_if.h"
+
+/*
+ * Bridge-specific data.
+ */
+struct apb_softc {
+ u_int8_t iomap;
+ u_int8_t memmap;
+};
+
+static int apb_probe(device_t dev);
+static int apb_attach(device_t dev);
+static struct resource *apb_alloc_resource(device_t dev, device_t child,
+ int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
+static int apb_route_interrupt(device_t pcib, device_t dev, int pin);
+
+static device_method_t apb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, apb_probe),
+ DEVMETHOD(device_attach, apb_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_read_ivar, pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, pcib_write_ivar),
+ DEVMETHOD(bus_alloc_resource, apb_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, pcib_maxslots),
+ DEVMETHOD(pcib_read_config, pcib_read_config),
+ DEVMETHOD(pcib_write_config, pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, apb_route_interrupt),
+
+ { 0, 0 }
+};
+
+static driver_t apb_driver = {
+ "pcib",
+ apb_methods,
+ sizeof(struct pcib_softc),
+};
+
+static devclass_t apb_devclass;
+
+DRIVER_MODULE(apb, pci, apb_driver, apb_devclass, 0, 0);
+
+#define APB_SOFTC(sc) ((struct apb_softc *)((sc)->extptr))
+
+/* APB specific registers */
+#define APBR_IOMAP 0xde
+#define APBR_MEMMAP 0xdf
+
+/* Definitions for the mapping registers */
+#define APB_IO_SCALE 0x200000
+#define APB_MEM_SCALE 0x20000000
+
+/*
+ * Generic device interface
+ */
+static int
+apb_probe(device_t dev)
+{
+
+ if (pci_get_vendor(dev) == 0x108e && /* Sun */
+ pci_get_device(dev) == 0x5000) { /* APB */
+ device_set_desc(dev, "APB PCI-PCI bridge");
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static void
+apb_map_print(u_int8_t map, u_long scale)
+{
+ int i, first;
+
+ for (first = 1, i = 0; i < 8; i++) {
+ if ((map & (1 << i)) != 0) {
+ printf("%s0x%lx-0x%lx", first ? "" : ", ",
+ i * scale, (i + 1) * scale - 1);
+ first = 0;
+ }
+ }
+}
+
+static int
+apb_map_checkrange(u_int8_t map, u_long scale, u_long start, u_long end)
+{
+ int i, ei;
+
+ i = start / scale;
+ ei = end / scale;
+ if (i > 7 || ei > 7)
+ return (0);
+ for (; i <= ei; i++)
+ if ((map & (1 << i)) == 0)
+ return (0);
+ return (1);
+}
+
+static int
+apb_attach(device_t dev)
+{
+ struct pcib_softc *sc;
+ struct apb_softc *asc;
+ device_t child;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->extptr = malloc(sizeof(struct apb_softc), M_DEVBUF, M_NOWAIT);
+ asc = APB_SOFTC(sc);
+
+ /*
+ * Get current bridge configuration.
+ */
+ sc->command = pci_read_config(dev, PCIR_COMMAND, 1);
+ sc->secbus = pci_read_config(dev, PCIR_SECBUS_1, 1);
+ sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1);
+ sc->secstat = pci_read_config(dev, PCIR_SECSTAT_1, 2);
+ sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2);
+ sc->seclat = pci_read_config(dev, PCIR_SECLAT_1, 1);
+
+ /* The APB does not implement base/limit registers. */
+ sc->iobase = sc->iolimit = 0;
+ sc->membase = sc->memlimit = 0;
+ sc->pmembase = sc->pmemlimit = 0;
+
+ asc->iomap = pci_read_config(dev, APBR_IOMAP, 1);
+ asc->memmap = pci_read_config(dev, APBR_MEMMAP, 1);
+
+ if (bootverbose) {
+ device_printf(dev, " secondary bus %d\n", sc->secbus);
+ device_printf(dev, " subordinate bus %d\n", sc->subbus);
+ device_printf(dev, " I/O decode ");
+ apb_map_print(asc->iomap, APB_IO_SCALE);
+ printf("\n");
+ device_printf(dev, " memory decode ");
+ apb_map_print(asc->memmap, APB_MEM_SCALE);
+ printf("\n");
+ }
+
+ /*
+ * XXX If the subordinate bus number is less than the secondary bus
+ * number, we should pick a better value. One sensible alternative
+ * would be to pick 255; the only tradeoff here is that configuration
+ * transactions would be more widely routed than absolutely necessary.
+ */
+ if (sc->secbus != 0) {
+ child = device_add_child(dev, "pci", -1);
+ if (child != NULL)
+ return (bus_generic_attach(dev));
+ } else
+ panic("apb_attach: APB with uninitialized secbus");
+
+ /* no secondary bus; we should have fixed this */
+ return (0);
+}
+
+/*
+ * We have to trap resource allocation requests and ensure that the bridge
+ * is set up to, or capable of handling them.
+ */
+static struct resource *
+apb_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct pcib_softc *sc;
+ struct apb_softc *asc;
+
+ sc = device_get_softc(dev);
+ asc = APB_SOFTC(sc);
+ /*
+ * If this is a "default" allocation against this rid, we can't work
+ * out where it's coming from (we should actually never see these) so we
+ * just have to punt.
+ */
+ if ((start == 0) && (end == ~0)) {
+ device_printf(dev, "can't decode default resource id %d for "
+ "%s%d, bypassing\n", *rid, device_get_name(child),
+ device_get_unit(child));
+ } else {
+ /*
+ * Fail the allocation for this range if it's not supported.
+ * XXX we should probably just fix up the bridge decode and
+ * soldier on.
+ */
+ switch (type) {
+ case SYS_RES_IOPORT:
+ if (!apb_map_checkrange(asc->iomap, APB_IO_SCALE, start,
+ end)) {
+ device_printf(dev, "device %s%d requested "
+ "unsupported I/O range 0x%lx-0x%lx\n",
+ device_get_name(child),
+ device_get_unit(child), start, end);
+ return (NULL);
+ }
+ if (bootverbose)
+ device_printf(sc->dev, "device %s%d requested "
+ "decoded I/O range 0x%lx-0x%lx\n",
+ device_get_name(child),
+ device_get_unit(child), start, end);
+ break;
+
+ case SYS_RES_MEMORY:
+ if (!apb_map_checkrange(asc->memmap, APB_MEM_SCALE,
+ start, end)) {
+ device_printf(dev, "device %s%d requested "
+ "unsupported memory range 0x%lx-0x%lx\n",
+ device_get_name(child),
+ device_get_unit(child), start, end);
+ return (NULL);
+ }
+ if (bootverbose)
+ device_printf(sc->dev, "device %s%d requested "
+ "decoded memory range 0x%lx-0x%lx\n",
+ device_get_name(child),
+ device_get_unit(child), start, end);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Bridge is OK decoding this resource, so pass it up.
+ */
+ return (bus_generic_alloc_resource(dev, child, type, rid, start, end,
+ count, flags));
+}
+
+/*
+ * Route an interrupt across a PCI bridge - the APB does not route interrupts,
+ * and routing of interrupts that are not preinitialized is not supported yet.
+ */
+static int
+apb_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+
+ panic("apb_route_interrupt");
+}
diff --git a/sys/sparc64/pci/ofw_pci.c b/sys/sparc64/pci/ofw_pci.c
new file mode 100644
index 0000000..67ee787
--- /dev/null
+++ b/sys/sparc64/pci/ofw_pci.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1999, 2000 Matthew R. Green
+ * All rights reserved.
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 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: NetBSD: psycho.c,v 1.35 2001/09/10 16:17:06 eeh Exp
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_ofw_pci.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/openfirm.h>
+
+#include <sparc64/pci/ofw_pci.h>
+
+#include <machine/ofw_bus.h>
+
+#include "pcib_if.h"
+
+/*
+ * Find the interrupt-map properties for a node. This might not be a property
+ * of the parent, because there may be bridges in between, so go up through the
+ * tree to find it.
+ * This seems to be only needed for PCI systems, so it has not been moved to
+ * ofw_bus.c
+ */
+int
+ofw_pci_find_imap(phandle_t node, struct ofw_pci_imap **imap,
+ struct ofw_pci_imap_msk *imapmsk)
+{
+ int nimap;
+
+ nimap = -1;
+ while ((node = OF_parent(node)) != 0) {
+ if ((nimap = OF_getprop_alloc(node, "interrupt-map",
+ sizeof(**imap), (void **)imap)) == -1 ||
+ OF_getprop(node, "interrupt-map-mask",
+ imapmsk, sizeof(*imapmsk)) == -1) {
+ if (*imap != NULL) {
+ free(*imap, M_OFWPROP);
+ *imap = NULL;
+ }
+ nimap = -1;
+ } else
+ break;
+ }
+ return (nimap);
+}
+
+/*
+ * Route an interrupt using the firmware nodes. Returns 255 for interrupts
+ * that cannot be routed (suitable for the PCI code).
+ */
+int
+ofw_pci_route_intr2(int intr, struct ofw_pci_register *pcir,
+ struct ofw_pci_imap *imap, int nimap, struct ofw_pci_imap_msk *imapmsk)
+{
+ char regm[12];
+ int cintr;
+
+ cintr = ofw_bus_route_intr(intr, pcir, sizeof(*pcir), 12, 1, imap,
+ nimap, imapmsk, regm);
+ if (cintr == -1)
+ return (255);
+ else
+ return (cintr);
+}
+
+int
+ofw_pci_route_intr(phandle_t node, struct ofw_pci_register *pcir,
+ struct ofw_pci_imap *intrmap, int nintrmap,
+ struct ofw_pci_imap_msk *intrmapmsk)
+{
+ int intr;
+
+ if (OF_getprop(node, "interrupts", &intr, sizeof(intr)) == -1)
+ return (255);
+
+ return (ofw_pci_route_intr2(intr, pcir, intrmap, nintrmap, intrmapmsk));
+}
+
+#define OFW_PCI_PCIBUS "pci"
+/*
+ * Walk the PCI bus hierarchy, starting with the root PCI bus and descending
+ * through bridges, and initialize the interrupt line configuration registers
+ * of attached devices using firmware information.
+ */
+void
+ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
+ int nintrmap, struct ofw_pci_imap_msk *intrmapmsk)
+{
+ struct ofw_pci_imap_msk lintrmapmsk;
+ struct ofw_pci_register pcir;
+ phandle_t node;
+ char type[32];
+ int intr;
+ int freemap;
+
+ if ((node = OF_child(bus)) == 0)
+ return;
+ freemap = 0;
+ do {
+ if (node == -1)
+ panic("ofw_pci_init_intr: OF_child failed");
+ if (OF_getprop(node, "device_type", type, sizeof(type)) == -1)
+ type[0] = '\0';
+ else
+ type[sizeof(type) - 1] = '\0';
+ if (strcmp(type, OFW_PCI_PCIBUS) == 0) {
+ /*
+ * This is a pci-pci bridge, recurse to initialize the
+ * child bus. The hierarchy is usually at most 2 levels
+ * deep, so recursion is feasible.
+ */
+#ifdef OFW_PCI_DEBUG
+ device_printf(dev, __func__": descending to "
+ "subordinate PCI bus\n");
+#endif
+ ofw_pci_init_intr(dev, node, NULL, 0, NULL);
+ } else {
+ if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
+ panic("ofw_pci_route_intr: OF_getprop failed");
+ /*
+ * If we didn't get interrupt map properties passed,
+ * try to find them now. On some systems, buses that
+ * have no non-bridge children have no such properties,
+ * so only try to find them at need.
+ */
+ if (intrmap == NULL) {
+ nintrmap = OF_getprop_alloc(bus,
+ "interrupt-map", sizeof(*intrmap),
+ (void **)&intrmap);
+ if (nintrmap == -1 ||
+ OF_getprop(bus, "interrupt-map-mask",
+ &lintrmapmsk, sizeof(lintrmapmsk)) == -1) {
+ panic("ofw_pci_init_intr: could not get "
+ "interrupt map properties");
+ }
+ intrmapmsk = &lintrmapmsk;
+ freemap = 1;
+ }
+ if ((intr = ofw_pci_route_intr(node, &pcir, intrmap,
+ nintrmap, intrmapmsk)) != 255) {
+#ifdef OFW_PCI_DEBUG
+ device_printf(dev, __func__": mapping intr for "
+ "%d/%d/%d to %d (preset was %d)\n",
+ OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
+ intr,
+ (int)PCIB_READ_CONFIG(dev,
+ OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
+ PCIR_INTLINE, 1));
+
+#endif /* OFW_PCI_DEBUG */
+ PCIB_WRITE_CONFIG(dev,
+ OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
+ PCIR_INTLINE, intr, 1);
+ } else {
+#ifdef OFW_PCI_DEBUG
+ device_printf(dev, __func__": no interrupt "
+ "mapping found for %d/%d/%d (preset %d)\n",
+ OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
+ (int)PCIB_READ_CONFIG(dev,
+ OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
+ PCIR_INTLINE, 1));
+#endif /* OFW_PCI_DEBUG */
+ /* The firmware initializes to 0 instead 255 */
+ PCIB_WRITE_CONFIG(dev,
+ OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
+ OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
+ PCIR_INTLINE, 255, 1);
+ }
+ }
+ } while ((node = OF_peer(node)) != 0);
+ if (freemap)
+ free(intrmap, M_OFWPROP);
+}
+
+phandle_t
+ofw_pci_find_node(int bus, int slot, int func)
+{
+ phandle_t node, bnode, parent;
+ struct ofw_pci_register pcir;
+ int br[2];
+ char name[16];
+
+ /* 1. Try to find the bus in question. */
+ bnode = 0;
+ name[sizeof(name) - 1] = '\0';
+ parent = OF_peer(0);
+ node = OF_child(parent);
+ while (node != 0 && node != -1) {
+ if (OF_getprop(node, "name", name, sizeof(name) - 1) != -1 &&
+ strcmp(name, "pci") == 0 &&
+ OF_getprop(node, "bus-range", br, sizeof(br)) != -1) {
+ /* Found the bus? */
+ if (bus == br[0]) {
+ bnode = node;
+ break;
+ }
+ /* Need to descend? */
+ if (bus > br[0] && bus <= br[1]) {
+ parent = node;
+ node = OF_child(node);
+ continue;
+ }
+ }
+ node = OF_peer(node);
+ }
+ if (bnode == 0)
+ return (0);
+ for (node = OF_child(bnode); node != 0 && node != -1;
+ node = OF_peer(node)) {
+ if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
+ continue;
+ if (OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi) == slot &&
+ OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi) == func) {
+ if (OFW_PCI_PHYS_HI_BUS(pcir.phys_hi) != bus)
+ panic("ofw_pci_find_node: bus number mismatch");
+ return (node);
+ }
+ }
+ return (0);
+}
diff --git a/sys/sparc64/pci/ofw_pci.h b/sys/sparc64/pci/ofw_pci.h
new file mode 100644
index 0000000..dc453be
--- /dev/null
+++ b/sys/sparc64/pci/ofw_pci.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1999, 2000 Matthew R. Green
+ * All rights reserved.
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 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: NetBSD: psychoreg.h,v 1.8 2001/09/10 16:17:06 eeh Exp
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SPARC64_PCI_OFW_PCI_H_
+#define _SPARC64_PCI_OFW_PCI_H_
+
+/* PCI range child spaces. XXX: are these MI? */
+#define PCI_CS_CONFIG 0x00
+#define PCI_CS_IO 0x01
+#define PCI_CS_MEM32 0x02
+#define PCI_CS_MEM64 0x03
+
+struct ofw_pci_imap {
+ u_int32_t phys_hi;
+ u_int32_t phys_mid;
+ u_int32_t phys_lo;
+ u_int32_t intr;
+ int32_t child_node;
+ u_int32_t child_intr;
+};
+
+struct ofw_pci_imap_msk {
+ u_int32_t phys_hi;
+ u_int32_t phys_mid;
+ u_int32_t phys_lo;
+ u_int32_t intr;
+};
+
+int ofw_pci_find_imap(phandle_t, struct ofw_pci_imap **,
+ struct ofw_pci_imap_msk *);
+int ofw_pci_route_intr2(int, struct ofw_pci_register *,
+ struct ofw_pci_imap *, int, struct ofw_pci_imap_msk *);
+int ofw_pci_route_intr(phandle_t, struct ofw_pci_register *,
+ struct ofw_pci_imap *, int, struct ofw_pci_imap_msk *);
+void ofw_pci_init_intr(device_t, phandle_t, struct ofw_pci_imap *, int,
+ struct ofw_pci_imap_msk *);
+phandle_t ofw_pci_find_node(int, int, int);
+int ofw_pci_dev_iterate_node(device_t, phandle_t, uintptr_t *, int, int *,
+ int *, uintptr_t *, uintptr_t *);
+int ofw_pci_dev_iterate(device_t, uintptr_t *, int, int *, int *, uintptr_t *,
+ uintptr_t *);
+
+#endif /* ! _SPARC64_PCI_OFW_PCI_H_ */
diff --git a/sys/sparc64/pci/psycho.c b/sys/sparc64/pci/psycho.c
new file mode 100644
index 0000000..d07af28
--- /dev/null
+++ b/sys/sparc64/pci/psycho.c
@@ -0,0 +1,1210 @@
+/*
+ * Copyright (c) 1999, 2000 Matthew R. Green
+ * All rights reserved.
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 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: NetBSD: psycho.c,v 1.35 2001/09/10 16:17:06 eeh Exp
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Support for `psycho' and `psycho+' UPA to PCI bridge and
+ * UltraSPARC IIi and IIe `sabre' PCI controllers.
+ */
+
+#include "opt_psycho.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <ofw/openfirm.h>
+#include <ofw/ofw_pci.h>
+
+#include <machine/bus.h>
+#include <machine/cache.h>
+#include <machine/iommureg.h>
+#include <machine/bus_common.h>
+#include <machine/frame.h>
+#include <machine/intr_machdep.h>
+#include <machine/nexusvar.h>
+#include <machine/ofw_upa.h>
+#include <machine/resource.h>
+
+#include <sys/rman.h>
+
+#include <machine/iommuvar.h>
+
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+
+#include <sparc64/pci/ofw_pci.h>
+#include <sparc64/pci/psychoreg.h>
+#include <sparc64/pci/psychovar.h>
+
+#include "pcib_if.h"
+#include "sparcbus_if.h"
+
+static void psycho_get_ranges(phandle_t, struct upa_ranges **, int *);
+static void psycho_set_intr(struct psycho_softc *, int, device_t, u_long *,
+ int, driver_intr_t);
+static int psycho_find_intrmap(struct psycho_softc *, int, u_long **,
+ u_long **, u_long *);
+static void psycho_intr_stub(void *);
+#ifdef PSYCHO_STRAY
+static void psycho_intr_stray(void *);
+#endif
+static bus_space_tag_t psycho_alloc_bus_tag(struct psycho_softc *, int);
+
+
+/* Interrupt handlers */
+static void psycho_ue(void *);
+static void psycho_ce(void *);
+static void psycho_bus_a(void *);
+static void psycho_bus_b(void *);
+static void psycho_powerfail(void *);
+#ifdef PSYCHO_MAP_WAKEUP
+static void psycho_wakeup(void *);
+#endif
+
+/* IOMMU support */
+static void psycho_iommu_init(struct psycho_softc *, int);
+
+/*
+ * bus space and bus dma support for UltraSPARC `psycho'. note that most
+ * of the bus dma support is provided by the iommu dvma controller.
+ */
+static int psycho_dmamem_alloc(bus_dma_tag_t, void **, int, bus_dmamap_t *);
+static void psycho_dmamem_free(bus_dma_tag_t, void *, bus_dmamap_t);
+static int psycho_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t,
+ bus_dmamap_callback_t *, void *, int);
+static void psycho_dmamap_unload(bus_dma_tag_t, bus_dmamap_t);
+static void psycho_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, bus_dmasync_op_t);
+
+/*
+ * autoconfiguration
+ */
+static int psycho_probe(device_t);
+static int psycho_attach(device_t);
+static int psycho_read_ivar(device_t, device_t, int, u_long *);
+static int psycho_setup_intr(device_t, device_t, struct resource *, int,
+ driver_intr_t *, void *, void **);
+static int psycho_teardown_intr(device_t, device_t, struct resource *, void *);
+static struct resource *psycho_alloc_resource(device_t, device_t, int, int *,
+ u_long, u_long, u_long, u_int);
+static int psycho_activate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int psycho_deactivate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int psycho_release_resource(device_t, device_t, int, int,
+ struct resource *);
+static int psycho_maxslots(device_t);
+static u_int32_t psycho_read_config(device_t, u_int, u_int, u_int, u_int, int);
+static void psycho_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t,
+ int);
+static int psycho_route_interrupt(device_t, device_t, int);
+static int psycho_intr_pending(device_t, int);
+static bus_space_handle_t psycho_get_bus_handle(device_t dev, enum sbbt_id id,
+ bus_space_handle_t childhdl, bus_space_tag_t *tag);
+
+static device_method_t psycho_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, psycho_probe),
+ DEVMETHOD(device_attach, psycho_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_read_ivar, psycho_read_ivar),
+ DEVMETHOD(bus_setup_intr, psycho_setup_intr),
+ DEVMETHOD(bus_teardown_intr, psycho_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, psycho_alloc_resource),
+ DEVMETHOD(bus_activate_resource, psycho_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, psycho_deactivate_resource),
+ DEVMETHOD(bus_release_resource, psycho_release_resource),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, psycho_maxslots),
+ DEVMETHOD(pcib_read_config, psycho_read_config),
+ DEVMETHOD(pcib_write_config, psycho_write_config),
+ DEVMETHOD(pcib_route_interrupt, psycho_route_interrupt),
+
+ /* sparcbus interface */
+ DEVMETHOD(sparcbus_intr_pending, psycho_intr_pending),
+ DEVMETHOD(sparcbus_get_bus_handle, psycho_get_bus_handle),
+
+ { 0, 0 }
+};
+
+static driver_t psycho_driver = {
+ "pcib",
+ psycho_methods,
+ sizeof(struct psycho_softc),
+};
+
+static devclass_t psycho_devclass;
+
+DRIVER_MODULE(psycho, nexus, psycho_driver, psycho_devclass, 0, 0);
+
+static int psycho_ndevs;
+static struct psycho_softc *psycho_softcs[4];
+
+struct psycho_clr {
+ u_long *pci_clr; /* clear register */
+ driver_intr_t *pci_handler; /* handler to call */
+ void *pci_arg; /* argument for the handler */
+ void *pci_cookie; /* interrupt cookie of parent bus */
+};
+
+/*
+ * "sabre" is the UltraSPARC IIi onboard UPA to PCI bridge. It manages a
+ * single PCI bus and does not have a streaming buffer. It often has an APB
+ * (advanced PCI bridge) connected to it, which was designed specifically for
+ * the IIi. The APB let's the IIi handle two independednt PCI buses, and
+ * appears as two "simba"'s underneath the sabre.
+ *
+ * "psycho" and "psycho+" is a dual UPA to PCI bridge. It sits on the UPA bus
+ * and manages two PCI buses. "psycho" has two 64-bit 33MHz buses, while
+ * "psycho+" controls both a 64-bit 33Mhz and a 64-bit 66Mhz PCI bus. You
+ * will usually find a "psycho+" since I don't think the original "psycho"
+ * ever shipped, and if it did it would be in the U30.
+ *
+ * Each "psycho" PCI bus appears as a separate OFW node, but since they are
+ * both part of the same IC, they only have a single register space. As such,
+ * they need to be configured together, even though the autoconfiguration will
+ * attach them separately.
+ *
+ * On UltraIIi machines, "sabre" itself usually takes pci0, with "simba" often
+ * as pci1 and pci2, although they have been implemented with other PCI bus
+ * numbers on some machines.
+ *
+ * On UltraII machines, there can be any number of "psycho+" ICs, each
+ * providing two PCI buses.
+ *
+ *
+ * XXXX The psycho/sabre node has an `interrupts' attribute. They contain
+ * the values of the following interrupts in this order:
+ *
+ * PCI Bus Error (30)
+ * DMA UE (2e)
+ * DMA CE (2f)
+ * Power Fail (25)
+ *
+ * We really should attach handlers for each.
+ */
+#define OFW_PCI_TYPE "pci"
+#define OFW_SABRE_MODEL "SUNW,sabre"
+#define OFW_SABRE_COMPAT "pci108e,a001"
+#define OFW_SIMBA_MODEL "SUNW,simba"
+#define OFW_PSYCHO_MODEL "SUNW,psycho"
+
+static int
+psycho_probe(device_t dev)
+{
+ phandle_t node;
+ char *dtype, *model;
+ static char compat[32];
+
+ node = nexus_get_node(dev);
+ if (OF_getprop(node, "compatible", compat, sizeof(compat)) == -1)
+ compat[0] = '\0';
+
+ dtype = nexus_get_device_type(dev);
+ model = nexus_get_model(dev);
+ /* match on a type of "pci" and a sabre or a psycho */
+ if (nexus_get_reg(dev) != NULL && dtype != NULL &&
+ strcmp(dtype, OFW_PCI_TYPE) == 0 &&
+ ((model != NULL && (strcmp(model, OFW_SABRE_MODEL) == 0 ||
+ strcmp(model, OFW_PSYCHO_MODEL) == 0)) ||
+ strcmp(compat, OFW_SABRE_COMPAT) == 0)) {
+ device_set_desc(dev, "U2P UPA-PCI bridge");
+ return (0);
+ }
+
+ return (ENXIO);
+}
+
+/*
+ * SUNW,psycho initialisation ..
+ * - find the per-psycho registers
+ * - figure out the IGN.
+ * - find our partner psycho
+ * - configure ourselves
+ * - bus range, bus,
+ * - interrupt map,
+ * - setup the chipsets.
+ * - if we're the first of the pair, initialise the IOMMU, otherwise
+ * just copy it's tags and addresses.
+ */
+static int
+psycho_attach(device_t dev)
+{
+ struct psycho_softc *sc;
+ struct psycho_softc *osc = NULL;
+ struct psycho_softc *asc;
+ struct ofw_nexus_reg *reg;
+ char compat[32];
+ char *model;
+ phandle_t node;
+ u_int64_t csr;
+ u_long pci_ctl;
+ int psycho_br[2];
+ int n, i, nreg;
+#if defined(PSYCHO_DEBUG) || defined(PSYCHO_STRAY)
+ u_long *map, *clr;
+#endif
+
+ bootverbose = 1;
+ node = nexus_get_node(dev);
+ sc = device_get_softc(dev);
+ if (OF_getprop(node, "compatible", compat, sizeof(compat)) == -1)
+ compat[0] = '\0';
+
+ sc->sc_node = node;
+ sc->sc_dev = dev;
+ sc->sc_bustag = nexus_get_bustag(dev);
+ sc->sc_dmatag = nexus_get_dmatag(dev);
+
+ /*
+ * call the model-specific initialisation routine.
+ */
+ model = nexus_get_model(dev);
+ if ((model != NULL &&
+ strcmp(model, OFW_SABRE_MODEL) == 0) ||
+ strcmp(compat, OFW_SABRE_COMPAT) == 0) {
+ sc->sc_mode = PSYCHO_MODE_SABRE;
+ if (model == NULL)
+ model = "sabre";
+ } else if (model != NULL &&
+ strcmp(model, OFW_PSYCHO_MODEL) == 0)
+ sc->sc_mode = PSYCHO_MODE_PSYCHO;
+ else
+ panic("psycho_attach: unknown model!");
+
+ /*
+ * The psycho gets three register banks:
+ * (0) per-PBM configuration and status registers
+ * (1) per-PBM PCI configuration space, containing only the
+ * PBM 256-byte PCI header
+ * (2) the shared psycho configuration registers (struct psychoreg)
+ *
+ * XXX use the prom address for the psycho registers? we do so far.
+ */
+ reg = nexus_get_reg(dev);
+ nreg = nexus_get_nreg(dev);
+ /* Register layouts are different. stuupid. */
+ if (sc->sc_mode == PSYCHO_MODE_PSYCHO) {
+ sc->sc_basepaddr = (vm_offset_t)reg[2].or_paddr;
+
+ if (nreg <= 2) {
+ panic("psycho_attach: %d not enough registers", nreg);
+ }
+ if (sparc64_bus_mem_map(UPA_BUS_SPACE, reg[2].or_paddr,
+ reg[2].or_len, 0, NULL, (void **)&sc->sc_regs))
+ panic("psycho_attach: cannot map regs");
+ pci_ctl = reg[0].or_paddr;
+ } else {
+ sc->sc_basepaddr = (vm_offset_t)reg[0].or_paddr;
+
+ if (nreg <= 0) {
+ panic("psycho_attach: %d not enough registers", nreg);
+ }
+ if (sparc64_bus_mem_map(UPA_BUS_SPACE, reg[0].or_paddr,
+ reg[0].or_len, 0, NULL, (void **)&sc->sc_regs))
+ panic("psycho_attach: cannot map regs");
+ pci_ctl = reg[0].or_paddr +
+ offsetof(struct psychoreg, psy_pcictl[0]);
+ }
+
+ csr = sc->sc_regs->psy_csr;
+ sc->sc_ign = 0x7c0; /* APB IGN is always 0x7c */
+ if (sc->sc_mode == PSYCHO_MODE_PSYCHO)
+ sc->sc_ign = PSYCHO_GCSR_IGN(csr) << 6;
+
+ device_printf(dev, "%s: impl %d, version %d: ign %x ",
+ model, PSYCHO_GCSR_IMPL(csr), PSYCHO_GCSR_VERS(csr),
+ sc->sc_ign);
+
+ /*
+ * Match other psycho's that are already configured against
+ * the base physical address. This will be the same for a
+ * pair of devices that share register space.
+ */
+ for (n = 0; n < psycho_ndevs && n < sizeof(psycho_softcs) /
+ sizeof(psycho_softcs[0]); n++) {
+ asc = (struct psycho_softc *)psycho_softcs[n];
+
+ if (asc == NULL || asc == sc)
+ /* This entry is not there or it is me */
+ continue;
+
+ if (asc->sc_basepaddr != sc->sc_basepaddr)
+ /* This is an unrelated psycho */
+ continue;
+
+ /* Found partner */
+ osc = asc;
+ break;
+ }
+
+ /*
+ * Setup the PCI control register
+ */
+ csr = bus_space_read_8(sc->sc_bustag,
+ (bus_space_handle_t)pci_ctl, offsetof(struct pci_ctl, pci_csr));
+ csr |= PCICTL_MRLM |
+ PCICTL_ARB_PARK |
+ PCICTL_ERRINTEN |
+ PCICTL_4ENABLE;
+ csr &= ~(PCICTL_SERR |
+ PCICTL_CPU_PRIO |
+ PCICTL_ARB_PRIO |
+ PCICTL_RTRYWAIT);
+ bus_space_write_8(sc->sc_bustag,
+ (bus_space_handle_t)pci_ctl, offsetof(struct pci_ctl, pci_csr),
+ csr);
+
+ /* grab the psycho ranges */
+ psycho_get_ranges(sc->sc_node, &sc->sc_range, &sc->sc_nrange);
+
+ /* get the bus-range for the psycho */
+ n = OF_getprop(node, "bus-range", (void *)psycho_br, sizeof(psycho_br));
+ if (n == -1)
+ panic("could not get psycho bus-range");
+ if (n != sizeof(psycho_br))
+ panic("broken psycho bus-range (%d)", n);
+
+ printf("bus range %u to %u; PCI bus %d\n", psycho_br[0], psycho_br[1],
+ psycho_br[0]);
+
+ sc->sc_busno = psycho_br[0];
+
+ /* Initialize memory and i/o rmans */
+ sc->sc_io_rman.rm_type = RMAN_ARRAY;
+ sc->sc_io_rman.rm_descr = "Psycho PCI I/O Ports";
+ if (rman_init(&sc->sc_io_rman) != 0 ||
+ rman_manage_region(&sc->sc_io_rman, 0, PSYCHO_IO_SIZE) != 0)
+ panic("psycho_probe: failed to set up i/o rman");
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ sc->sc_mem_rman.rm_descr = "Psycho PCI Memory";
+ if (rman_init(&sc->sc_mem_rman) != 0 ||
+ rman_manage_region(&sc->sc_mem_rman, 0, PSYCHO_MEM_SIZE) != 0)
+ panic("psycho_probe: failed to set up memory rman");
+ /*
+ * Find the addresses of the various bus spaces.
+ * There should not be multiple ones of one kind.
+ * The physical start addresses of the ranges are the configuration,
+ * memory and IO handles.
+ */
+ for (n = 0; n < sc->sc_nrange; n++) {
+ i = UPA_RANGE_CS(&sc->sc_range[n]);
+ if (sc->sc_bh[i] != 0)
+ panic("psycho_attach: duplicate range for space %d", i);
+ sc->sc_bh[i] = UPA_RANGE_PHYS(&sc->sc_range[n]);
+ }
+ /*
+ * Check that all needed handles are present. The PCI_CS_MEM64 one is
+ * not currently used.
+ */
+ for (n = 0; n < 3; n++) {
+ if (sc->sc_bh[n] == 0)
+ panic("psycho_attach: range %d missing", n);
+ }
+
+ /* allocate our tags */
+ sc->sc_memt = psycho_alloc_bus_tag(sc, PCI_MEMORY_BUS_SPACE);
+ sc->sc_iot = psycho_alloc_bus_tag(sc, PCI_IO_BUS_SPACE);
+ sc->sc_cfgt = psycho_alloc_bus_tag(sc, PCI_CONFIG_BUS_SPACE);
+ if (bus_dma_tag_create(sc->sc_dmatag, 8, 1, 0, 0x3ffffffff, NULL, NULL,
+ 0x3ffffffff, 0xff, 0xffffffff, 0, &sc->sc_dmat) != 0)
+ panic("bus_dma_tag_create failed");
+ /* Customize the tag */
+ sc->sc_dmat->cookie = sc;
+ sc->sc_dmat->dmamem_alloc = psycho_dmamem_alloc;
+ sc->sc_dmat->dmamem_free = psycho_dmamem_free;
+ sc->sc_dmat->dmamap_load = psycho_dmamap_load;
+ sc->sc_dmat->dmamap_unload = psycho_dmamap_unload;
+ sc->sc_dmat->dmamap_sync = psycho_dmamap_sync;
+ /* XXX: register as root dma tag (kluge). */
+ sparc64_root_dma_tag = sc->sc_dmat;
+
+ if ((sc->sc_nintrmap = OF_getprop_alloc(sc->sc_node, "interrupt-map",
+ sizeof(*sc->sc_intrmap), (void **)&sc->sc_intrmap)) == -1 ||
+ OF_getprop(sc->sc_node, "interrupt-map-mask", &sc->sc_intrmapmsk,
+ sizeof(sc->sc_intrmapmsk)) == -1) {
+ if (sc->sc_intrmap != NULL) {
+ free(sc->sc_intrmap, M_OFWPROP);
+ sc->sc_intrmap = NULL;
+ }
+ }
+
+ /* Register the softc, this is needed for paired psychos. */
+ if (psycho_ndevs < sizeof(psycho_softcs) / sizeof(psycho_softcs[0]))
+ psycho_softcs[psycho_ndevs] = sc;
+ else
+ device_printf(dev, "XXX: bump the number of psycho_softcs");
+ psycho_ndevs++;
+ /*
+ * And finally, if we're a sabre or the first of a pair of psycho's to
+ * arrive here, start up the IOMMU and get a config space tag.
+ */
+ if (osc == NULL) {
+ /*
+ * Establish handlers for interesting interrupts....
+ *
+ * XXX We need to remember these and remove this to support
+ * hotplug on the UPA/FHC bus.
+ *
+ * XXX Not all controllers have these, but installing them
+ * is better than trying to sort through this mess.
+ */
+ psycho_set_intr(sc, 0, dev, &sc->sc_regs->ue_int_map,
+ INTR_TYPE_MISC | INTR_FAST, psycho_ue);
+ psycho_set_intr(sc, 1, dev, &sc->sc_regs->ce_int_map,
+ INTR_TYPE_MISC, psycho_ce);
+ psycho_set_intr(sc, 2, dev,
+ &sc->sc_regs->pciaerr_int_map, INTR_TYPE_MISC | INTR_FAST,
+ psycho_bus_a);
+ psycho_set_intr(sc, 3, dev,
+ &sc->sc_regs->pciberr_int_map, INTR_TYPE_MISC | INTR_FAST,
+ psycho_bus_b);
+ psycho_set_intr(sc, 4, dev, &sc->sc_regs->power_int_map,
+ INTR_TYPE_MISC | INTR_FAST, psycho_powerfail);
+#ifdef PSYCHO_MAP_WAKEUP
+ /*
+ * On some models, this is mapped to the same interrupt as
+ * pciberr by default, so leave it alone for now since
+ * psycho_wakeup() doesn't do anything useful anyway.
+ */
+ psycho_set_intr(sc, 5, dev, &sc->sc_regs->pwrmgt_int_map,
+ INTR_TYPE_MISC, psycho_wakeup);
+#endif /* PSYCHO_MAP_WAKEUP */
+
+ /*
+ * Setup IOMMU and PCI configuration if we're the first
+ * of a pair of psycho's to arrive here.
+ *
+ * We should calculate a TSB size based on amount of RAM
+ * and number of bus controllers and number an type of
+ * child devices.
+ *
+ * For the moment, 32KB should be more than enough.
+ */
+ psycho_iommu_init(sc, 2);
+ } else {
+ /* Just copy IOMMU state, config tag and address */
+ sc->sc_is = osc->sc_is;
+ }
+
+ /*
+ * Enable all interrupts, clear all interrupt states, and install an
+ * interrupt handler for OBIO interrupts, which can be ISA ones
+ * (to frob the interrupt clear registers).
+ * This aids the debugging of interrupt routing problems, and is needed
+ * for isa drivers that use isa_irq_pending (otherwise the registers
+ * will never be cleared).
+ */
+#if defined(PSYCHO_DEBUG) || defined(PSYCHO_STRAY)
+ for (map = &sc->sc_regs->pcia0_int_map,
+ clr = &sc->sc_regs->pcia0_int_clr[0], n = 0;
+ map <= &sc->sc_regs->pcib3_int_map; map++, clr += 4, n++) {
+#ifdef PSYCHO_DEBUG
+ device_printf(dev, "intr map (pci) %d: %lx\n", n, *map);
+#endif
+ *map &= ~INTMAP_V;
+ membar(StoreStore);
+ for (i = 0; i < 4; i++)
+ clr[i] = 0;
+ membar(StoreStore);
+ *map |= INTMAP_V;
+ }
+ for (map = &sc->sc_regs->scsi_int_map, n = 0,
+ clr = &sc->sc_regs->scsi_int_clr, n = 0;
+ map <= &sc->sc_regs->ffb1_int_map; map++, clr++, n++) {
+#ifdef PSYCHO_DEBUG
+ device_printf(dev, "intr map (obio) %d: %lx, clr: %p\n", n,
+ *map, clr);
+#endif
+ *map &= ~INTMAP_V;
+ membar(StoreStore);
+ *clr = 0;
+#ifdef PSYCHO_STRAY
+ /*
+ * This can cause interrupt storms, and is therefore disabled
+ * by default.
+ * XXX: use intr_setup() to not confuse higher level code
+ */
+ if (INTVEC(*map) != 0x7e6 && INTVEC(*map) != 0x7e7 &&
+ INTVEC(*map) != 0)
+ intr_setup(PIL_LOW, intr_dequeue, INTVEC(*map), psycho_intr_stray,
+ (void *)clr);
+#endif
+ membar(StoreStore);
+ *map |= INTMAP_V;
+ }
+#endif
+
+ /*
+ * Initialize the interrupt registers of all devices hanging from
+ * the host bridge directly or indirectly via PCI-PCI bridges.
+ * The MI code (and the PCI spec) assume that this is done during
+ * system initialization, however the firmware does not do this
+ * at least on some models, and we probably shouldn't trust that
+ * the firmware uses the same model as this driver if it does.
+ */
+ ofw_pci_init_intr(dev, sc->sc_node, sc->sc_intrmap, sc->sc_nintrmap,
+ &sc->sc_intrmapmsk);
+
+ device_add_child(dev, "pci", device_get_unit(dev));
+ return (bus_generic_attach(dev));
+}
+
+static void
+psycho_set_intr(struct psycho_softc *sc, int index,
+ device_t dev, u_long *map, int iflags, driver_intr_t handler)
+{
+ int rid;
+
+ sc->sc_irq[index] = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
+ INTVEC(*map), INTVEC(*map), 1, RF_ACTIVE);
+ if (sc->sc_irq[index] == NULL)
+ panic("psycho_set_intr: failed to get interupt");
+ bus_setup_intr(dev, sc->sc_irq[index], INTR_TYPE_MISC | iflags,
+ handler, sc, &sc->sc_ihand[index]);
+ *map |= INTMAP_V;
+}
+
+static int
+psycho_find_intrmap(struct psycho_softc *sc, int ino, u_long **intrmapptr,
+ u_long **intrclrptr, u_long *intrdiagptr)
+{
+ u_long *intrmap, *intrclr, diag;
+ int found;
+
+ found = 0;
+ /* Hunt thru obio first */
+ diag = sc->sc_regs->obio_int_diag;
+ for (intrmap = &sc->sc_regs->scsi_int_map,
+ intrclr = &sc->sc_regs->scsi_int_clr;
+ intrmap <= &sc->sc_regs->ffb1_int_map;
+ intrmap++, intrclr++, diag >>= 2) {
+ if (INTINO(*intrmap) == ino) {
+ diag &= 2;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ diag = sc->sc_regs->pci_int_diag;
+ /* Now do PCI interrupts */
+ for (intrmap = &sc->sc_regs->pcia0_int_map,
+ intrclr = &sc->sc_regs->pcia0_int_clr[0];
+ intrmap <= &sc->sc_regs->pcib3_int_map;
+ intrmap++, intrclr += 4, diag >>= 8) {
+ if (((*intrmap ^ ino) & 0x3c) == 0) {
+ intrclr += ino & 3;
+ diag = (diag >> ((ino & 3) * 2)) & 2;
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (intrmapptr != NULL)
+ *intrmapptr = intrmap;
+ if (intrclrptr != NULL)
+ *intrclrptr = intrclr;
+ if (intrdiagptr != NULL)
+ *intrdiagptr = diag;
+ return (found);
+}
+
+/* grovel the OBP for various psycho properties */
+static void
+psycho_get_ranges(phandle_t node, struct upa_ranges **rp, int *np)
+{
+
+ *np = OF_getprop_alloc(node, "ranges", sizeof(**rp), (void **)rp);
+ if (*np == -1)
+ panic("could not get psycho ranges");
+}
+
+/*
+ * Interrupt handlers.
+ */
+static void
+psycho_ue(void *arg)
+{
+ struct psycho_softc *sc = (struct psycho_softc *)arg;
+ struct psychoreg *regs = sc->sc_regs;
+
+ sc->sc_regs->ue_int_clr = 0;
+ /* It's uncorrectable. Dump the regs and panic. */
+ panic("%s: uncorrectable DMA error AFAR %llx AFSR %llx\n",
+ device_get_name(sc->sc_dev),
+ (long long)regs->psy_ue_afar, (long long)regs->psy_ue_afsr);
+}
+
+static void
+psycho_ce(void *arg)
+{
+ struct psycho_softc *sc = (struct psycho_softc *)arg;
+ struct psychoreg *regs = sc->sc_regs;
+
+ sc->sc_regs->ce_int_clr = 0;
+ /* It's correctable. Dump the regs and continue. */
+ printf("%s: correctable DMA error AFAR %llx AFSR %llx\n",
+ device_get_name(sc->sc_dev),
+ (long long)regs->psy_ce_afar, (long long)regs->psy_ce_afsr);
+}
+
+static void
+psycho_bus_a(void *arg)
+{
+ struct psycho_softc *sc = (struct psycho_softc *)arg;
+ struct psychoreg *regs = sc->sc_regs;
+
+ sc->sc_regs->pciaerr_int_clr = 0;
+ /* It's uncorrectable. Dump the regs and panic. */
+ panic("%s: PCI bus A error AFAR %lx AFSR %lx\n",
+ device_get_name(sc->sc_dev),
+ regs->psy_pcictl[0].pci_afar, regs->psy_pcictl[0].pci_afsr);
+}
+
+static void
+psycho_bus_b(void *arg)
+{
+ struct psycho_softc *sc = (struct psycho_softc *)arg;
+ struct psychoreg *regs = sc->sc_regs;
+
+ sc->sc_regs->pciberr_int_clr = 0;
+ /* It's uncorrectable. Dump the regs and panic. */
+ panic("%s: PCI bus B error AFAR %lx AFSR %lx\n",
+ device_get_name(sc->sc_dev),
+ regs->psy_pcictl[1].pci_afar, regs->psy_pcictl[1].pci_afsr);
+}
+
+static void
+psycho_powerfail(void *arg)
+{
+ struct psycho_softc *sc = (struct psycho_softc *)arg;
+
+ sc->sc_regs->power_int_clr = 0;
+ /* We lost power. Try to shut down NOW. */
+ printf("Power Failure Detected: Shutting down NOW.\n");
+ shutdown_nice(0);
+}
+
+#ifdef PSYCHO_MAP_WAKEUP
+static void
+psycho_wakeup(void *arg)
+{
+ struct psycho_softc *sc = (struct psycho_softc *)arg;
+
+ sc->sc_regs->pwrmgt_int_clr = 0;
+ /* Gee, we don't really have a framework to deal with this properly. */
+ printf("%s: power management wakeup\n", device_get_name(sc->sc_dev));
+}
+#endif /* PSYCHO_MAP_WAKEUP */
+
+/* initialise the IOMMU... */
+void
+psycho_iommu_init(struct psycho_softc *sc, int tsbsize)
+{
+ char *name;
+ struct iommu_state *is;
+ u_int32_t iobase = -1;
+ int *vdma = NULL;
+ int nitem;
+
+ is = malloc(sizeof(struct iommu_state), M_DEVBUF, M_NOWAIT);
+ if (is == NULL)
+ panic("psycho_iommu_init: malloc failed");
+
+ sc->sc_is = is;
+
+ /* punch in our copies */
+ is->is_bustag = sc->sc_bustag;
+ is->is_iommu = &sc->sc_regs->psy_iommu;
+ is->is_dtag = &sc->sc_regs->tlb_tag_diag[0];
+ is->is_ddram = &sc->sc_regs->tlb_data_diag[0];
+ is->is_dqueue = &sc->sc_regs->iommu_queue_diag[0];
+ is->is_dva = &sc->sc_regs->iommu_svadiag;
+ is->is_dtcmp = &sc->sc_regs->iommu_tlb_comp_diag;
+
+ if (OF_getproplen(sc->sc_node, "no-streaming-cache") < 0)
+ is->is_sb = 0;
+ else
+ is->is_sb = &sc->sc_regs->psy_iommu_strbuf;
+
+ /*
+ * Separate the men from the boys. Get the `virtual-dma'
+ * property for sabre and use that to make sure the damn
+ * iommu works.
+ *
+ * We could query the `#virtual-dma-size-cells' and
+ * `#virtual-dma-addr-cells' and DTRT, but I'm lazy.
+ */
+ nitem = OF_getprop_alloc(sc->sc_node, "virtual-dma", sizeof(vdma),
+ (void **)&vdma);
+ if (nitem > 0) {
+ iobase = vdma[0];
+ tsbsize = ffs(vdma[1]);
+ if (tsbsize < 25 || tsbsize > 31 ||
+ (vdma[1] & ~(1 << (tsbsize - 1))) != 0) {
+ printf("bogus tsb size %x, using 7\n", vdma[1]);
+ tsbsize = 31;
+ }
+ tsbsize -= 24;
+ free(vdma, M_OFWPROP);
+ }
+
+ /* give us a nice name.. */
+ name = (char *)malloc(32, M_DEVBUF, M_NOWAIT);
+ if (name == 0)
+ panic("couldn't malloc iommu name");
+ snprintf(name, 32, "%s dvma", device_get_name(sc->sc_dev));
+
+ iommu_init(name, is, tsbsize, iobase);
+}
+
+static int
+psycho_maxslots(device_t dev)
+{
+
+ /*
+ * XXX: is this correct? At any rate, a number that is too high
+ * shouldn't do any harm, if only because of the way things are
+ * handled in psycho_read_config.
+ */
+ return (31);
+}
+
+static u_int32_t
+psycho_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
+ int width)
+{
+ struct psycho_softc *sc;
+ bus_space_handle_t bh;
+ u_long offset = 0;
+ u_int32_t r;
+
+ /*
+ * The psycho bridge does not tolerate accesses to unconfigured PCI
+ * devices' or function's config space, so look up the device in the
+ * first, and if it is not present, return a value that will make the
+ * detection think that there is no device here. This is somehow ugly...
+ */
+ if (ofw_pci_find_node(bus, slot, func) == 0)
+ return (0xffffffff);
+ sc = (struct psycho_softc *)device_get_softc(dev);
+ offset = PSYCHO_CONF_OFF(bus, slot, func, reg);
+ bh = sc->sc_bh[PCI_CS_CONFIG];
+ switch (width) {
+ case 1:
+ r = bus_space_read_1(sc->sc_cfgt, bh, offset);
+ break;
+ case 2:
+ r = bus_space_read_2(sc->sc_cfgt, bh, offset);
+ break;
+ case 4:
+ r = bus_space_read_4(sc->sc_cfgt, bh, offset);
+ break;
+ default:
+ panic("psycho_read_config: bad width");
+ }
+ return (r);
+}
+
+static void
+psycho_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, u_int32_t val, int width)
+{
+ struct psycho_softc *sc;
+ bus_space_handle_t bh;
+ u_long offset = 0;
+
+ sc = (struct psycho_softc *)device_get_softc(dev);
+ offset = PSYCHO_CONF_OFF(bus, slot, func, reg);
+ bh = sc->sc_bh[PCI_CS_CONFIG];
+ switch (width) {
+ case 1:
+ bus_space_write_1(sc->sc_cfgt, bh, offset, val);
+ break;
+ case 2:
+ bus_space_write_2(sc->sc_cfgt, bh, offset, val);
+ break;
+ case 4:
+ bus_space_write_4(sc->sc_cfgt, bh, offset, val);
+ break;
+ default:
+ panic("psycho_write_config: bad width");
+ }
+}
+
+static int
+psycho_route_interrupt(device_t bus, device_t dev, int pin)
+{
+
+ /*
+ * Since we preinitialize all interrupt line registers, this should not
+ * happen for any built-in device.
+ * Devices on bridges that route interrupts cannot work now - the
+ * interrupt pin mappings are not known from the firmware...
+ */
+ panic("psycho_route_interrupt");
+}
+
+static int
+psycho_read_ivar(device_t dev, device_t child, int which, u_long *result)
+{
+ struct psycho_softc *sc;
+
+ sc = (struct psycho_softc *)device_get_softc(dev);
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_busno;
+ return (0);
+ }
+ return (ENOENT);
+}
+
+/* Write to the correct clr register, and call the actual handler. */
+static void
+psycho_intr_stub(void *arg)
+{
+ struct psycho_clr *pc;
+
+ pc = (struct psycho_clr *)arg;
+ *pc->pci_clr = 0;
+ pc->pci_handler(pc->pci_arg);
+}
+
+#ifdef PSYCHO_STRAY
+/*
+ * Write to the correct clr register and return. arg is the address of the clear
+ * register to be used.
+ * XXX: print a message?
+ */
+static void
+psycho_intr_stray(void *arg)
+{
+
+ *((u_long *)arg) = 0;
+}
+#endif
+
+static int
+psycho_setup_intr(device_t dev, device_t child,
+ struct resource *ires, int flags, driver_intr_t *intr, void *arg,
+ void **cookiep)
+{
+ struct psycho_softc *sc;
+ struct psycho_clr *pc;
+ u_long *intrmapptr, *intrclrptr;
+ int ino;
+ int error;
+ long vec = rman_get_start(ires);
+
+ sc = (struct psycho_softc *)device_get_softc(dev);
+ pc = (struct psycho_clr *)
+ malloc(sizeof(*pc), M_DEVBUF, M_NOWAIT);
+ if (pc == NULL)
+ return (NULL);
+
+ /*
+ * Hunt through all the interrupt mapping regs to look for our
+ * interrupt vector.
+ *
+ * XXX We only compare INOs rather than IGNs since the firmware may
+ * not provide the IGN and the IGN is constant for all device on that
+ * PCI controller. This could cause problems for the FFB/external
+ * interrupt which has a full vector that can be set arbitrarily.
+ */
+ ino = INTINO(vec);
+
+ if (!psycho_find_intrmap(sc, ino, &intrmapptr, &intrclrptr, NULL)) {
+ printf("Cannot find interrupt vector %lx\n", vec);
+ free(pc, M_DEVBUF);
+ return (NULL);
+ }
+
+#ifdef PSYCHO_DEBUG
+ device_printf(dev, "psycho_setup_intr: INO %d, map %p, clr %p\n", ino,
+ intrmapptr, intrclrptr);
+#endif
+ pc->pci_arg = arg;
+ pc->pci_handler = intr;
+ pc->pci_clr = intrclrptr;
+ /* Disable the interrupt while we fiddle with it */
+ *intrmapptr &= ~INTMAP_V;
+ membar(Sync);
+ error = bus_setup_intr(dev, ires, flags, psycho_intr_stub, pc,
+ cookiep);
+ if (error != 0) {
+ free(pc, M_DEVBUF);
+ return (error);
+ }
+ pc->pci_cookie = *cookiep;
+ *cookiep = pc;
+
+ /*
+ * Clear the interrupt, it might have been triggered before it was
+ * set up.
+ */
+ *intrclrptr = 0;
+ membar(StoreStore);
+ /*
+ * Enable the interrupt now we have the handler installed.
+ * Read the current value as we can't change it besides the
+ * valid bit so so make sure only this bit is changed.
+ */
+ *intrmapptr |= INTMAP_V;
+ return (error);
+}
+
+static int
+psycho_teardown_intr(device_t dev, device_t child,
+ struct resource *vec, void *cookie)
+{
+ struct psycho_clr *pc;
+ int error;
+
+ pc = (struct psycho_clr *)cookie;
+ error = bus_teardown_intr(dev, vec, pc->pci_cookie);
+ /*
+ * Don't disable the interrupt for now, so that stray interupts get
+ * detected...
+ */
+ if (error != 0)
+ free(pc, M_DEVBUF);
+ return (error);
+}
+
+static struct resource *
+psycho_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 psycho_softc *sc;
+ struct resource *rv;
+ struct rman *rm;
+ bus_space_tag_t bt;
+ bus_space_handle_t bh;
+ int needactivate = flags & RF_ACTIVE;
+
+ flags &= ~RF_ACTIVE;
+
+ sc = (struct psycho_softc *)device_get_softc(bus);
+ if (type == SYS_RES_IRQ) {
+ /*
+ * XXX: Don't accept blank ranges for now, only single
+ * interrupts. The other case should not happen with the MI pci
+ * code...
+ * XXX: This may return a resource that is out of the range
+ * that was specified. Is this correct...?
+ */
+ if (start != end)
+ panic("psycho_alloc_resource: XXX: interrupt range");
+ start = end |= sc->sc_ign;
+ return (bus_alloc_resource(bus, type, rid, start, end,
+ count, flags));
+ }
+ switch (type) {
+ case SYS_RES_MEMORY:
+ rm = &sc->sc_mem_rman;
+ bt = sc->sc_memt;
+ bh = sc->sc_bh[PCI_CS_MEM32];
+ break;
+ case SYS_RES_IOPORT:
+ rm = &sc->sc_io_rman;
+ bt = sc->sc_iot;
+ /* XXX: probably should use ranges property here. */
+ bh = sc->sc_bh[PCI_CS_IO];
+ break;
+ default:
+ return (NULL);
+ }
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (rv == NULL)
+ return (NULL);
+
+ bh += rman_get_start(rv);
+ rman_set_bustag(rv, bt);
+ rman_set_bushandle(rv, bh);
+
+ if (needactivate) {
+ if (bus_activate_resource(child, type, *rid, rv)) {
+ rman_release_resource(rv);
+ return (NULL);
+ }
+ }
+
+ return (rv);
+}
+
+static int
+psycho_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+
+ if (type == SYS_RES_IRQ)
+ return (bus_activate_resource(bus, type, rid, r));
+ return (rman_activate_resource(r));
+}
+
+static int
+psycho_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+
+ if (type == SYS_RES_IRQ)
+ return (bus_deactivate_resource(bus, type, rid, r));
+ return (rman_deactivate_resource(r));
+}
+
+static int
+psycho_release_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+ int error;
+
+ if (type == SYS_RES_IRQ)
+ return (bus_release_resource(bus, type, rid, r));
+ if (rman_get_flags(r) & RF_ACTIVE) {
+ error = bus_deactivate_resource(child, type, rid, r);
+ if (error)
+ return error;
+ }
+ return (rman_release_resource(r));
+}
+
+static int
+psycho_intr_pending(device_t dev, int intr)
+{
+ struct psycho_softc *sc;
+ u_long diag;
+
+ sc = (struct psycho_softc *)device_get_softc(dev);
+ if (!psycho_find_intrmap(sc, intr, NULL, NULL, &diag)) {
+ printf("psycho_intr_pending: mapping not found for %d\n", intr);
+ return (0);
+ }
+ return (diag != 0);
+}
+
+static bus_space_handle_t
+psycho_get_bus_handle(device_t dev, enum sbbt_id id,
+ bus_space_handle_t childhdl, bus_space_tag_t *tag)
+{
+ struct psycho_softc *sc;
+
+ sc = (struct psycho_softc *)device_get_softc(dev);
+ switch(id) {
+ case SBBT_IO:
+ *tag = sc->sc_iot;
+ return (sc->sc_bh[PCI_CS_IO] + childhdl);
+ case SBBT_MEM:
+ *tag = sc->sc_memt;
+ return (sc->sc_bh[PCI_CS_MEM32] + childhdl);
+ default:
+ panic("psycho_get_bus_handle: illegal space\n");
+ }
+}
+
+/*
+ * below here is bus space and bus dma support
+ */
+static bus_space_tag_t
+psycho_alloc_bus_tag(struct psycho_softc *sc, int type)
+{
+ bus_space_tag_t bt;
+
+ bt = (bus_space_tag_t)
+ malloc(sizeof(struct bus_space_tag), M_DEVBUF, M_NOWAIT);
+ if (bt == NULL)
+ panic("could not allocate psycho bus tag");
+
+ bzero(bt, sizeof *bt);
+ bt->cookie = sc;
+ bt->parent = sc->sc_bustag;
+ bt->type = type;
+ return (bt);
+}
+
+/*
+ * hooks into the iommu dvma calls.
+ */
+static int
+psycho_dmamem_alloc(bus_dma_tag_t dmat, void **vaddr, int flags, bus_dmamap_t *mapp)
+{
+ struct psycho_softc *sc;
+
+ sc = (struct psycho_softc *)dmat->cookie;
+ return (iommu_dvmamem_alloc(dmat, sc->sc_is, vaddr, flags, mapp));
+}
+
+static void
+psycho_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
+{
+ struct psycho_softc *sc;
+
+ sc = (struct psycho_softc *)dmat->cookie;
+ return (iommu_dvmamem_free(dmat, sc->sc_is, vaddr, map));
+}
+
+static int
+psycho_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
+ bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg,
+ int flags)
+{
+ struct psycho_softc *sc;
+
+ sc = (struct psycho_softc *)dmat->cookie;
+ return (iommu_dvmamap_load(dmat, sc->sc_is, map, buf, buflen, callback,
+ callback_arg, flags));
+}
+
+static void
+psycho_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ struct psycho_softc *sc;
+
+ sc = (struct psycho_softc *)dmat->cookie;
+ iommu_dvmamap_unload(dmat, sc->sc_is, map);
+}
+
+static void
+psycho_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map,
+ bus_dmasync_op_t op)
+{
+ struct psycho_softc *sc;
+
+ sc = (struct psycho_softc *)dmat->cookie;
+ iommu_dvmamap_sync(dmat, sc->sc_is, map, op);
+}
diff --git a/sys/sparc64/pci/psychoreg.h b/sys/sparc64/pci/psychoreg.h
new file mode 100644
index 0000000..9be0205
--- /dev/null
+++ b/sys/sparc64/pci/psychoreg.h
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 1998, 1999 Eduardo E. Horvath
+ * Copyright (c) 1999 Matthew R. Green
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 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: NetBSD: psychoreg.h,v 1.8 2001/09/10 16:17:06 eeh Exp
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SPARC64_PCI_PSYCHOREG_H_
+#define _SPARC64_PCI_PSYCHOREG_H_
+
+/*
+ * Sun4u PCI definitions. Here's where we deal w/the machine
+ * dependencies of psycho and the PCI controller on the UltraIIi.
+ *
+ * All PCI registers are bit-swapped, however they are not byte-swapped.
+ * This means that they must be accessed using little-endian access modes,
+ * either map the pages little-endian or use little-endian ASIs.
+ *
+ * PSYCHO implements two PCI buses, A and B.
+ */
+
+struct psychoreg {
+ struct upareg {
+ /* UPA port ID register */ /* 1fe.0000.0000 */
+ u_int64_t upa_portid;
+ /* UPA config register */ /* 1fe.0000.0008 */
+ u_int64_t upa_config;
+ } sys_upa;
+
+ /* PSYCHO control/status register */ /* 1fe.0000.0010 */
+ u_int64_t psy_csr;
+ /*
+ * 63 59 55 50 45 4 3 2 1 0
+ * +------+------+------+------+--//---+--------+-------+-----+------+
+ * | IMPL | VERS | MID | IGN | xxx | APCKEN | APERR | IAP | MODE |
+ * +------+------+------+------+--//---+--------+-------+-----+------+
+ *
+ */
+#define PSYCHO_GCSR_IMPL(csr) ((u_int)(((csr) >> 60) & 0xf))
+#define PSYCHO_GCSR_VERS(csr) ((u_int)(((csr) >> 56) & 0xf))
+#define PSYCHO_GCSR_MID(csr) ((u_int)(((csr) >> 51) & 0x1f))
+#define PSYCHO_GCSR_IGN(csr) ((u_int)(((csr) >> 46) & 0x1f))
+#define PSYCHO_CSR_APCKEN 8 /* UPA addr parity check enable */
+#define PSYCHO_CSR_APERR 4 /* UPA addr parity error */
+#define PSYCHO_CSR_IAP 2 /* invert UPA address parity */
+#define PSYCHO_CSR_MODE 1 /* UPA/PCI handshake */
+
+ u_int64_t pad0;
+ /* ECC control register */ /* 1fe.0000.0020 */
+ u_int64_t psy_ecccr;
+ /* 1fe.0000.0028 */
+ u_int64_t reserved;
+ /* Uncorrectable Error AFSR */ /* 1fe.0000.0030 */
+ u_int64_t psy_ue_afsr;
+ /* Uncorrectable Error AFAR */ /* 1fe.0000.0038 */
+ u_int64_t psy_ue_afar;
+ /* Correctable Error AFSR */ /* 1fe.0000.0040 */
+ u_int64_t psy_ce_afsr;
+ /* Correctable Error AFAR */ /* 1fe.0000.0048 */
+ u_int64_t psy_ce_afar;
+
+ u_int64_t pad1[22];
+
+ struct perfmon {
+ /* Performance monitor control reg */ /* 1fe.0000.0100 */
+ u_int64_t pm_cr;
+ /* Performance monitor counter reg */ /* 1fe.0000.0108 */
+ u_int64_t pm_count;
+ } psy_pm;
+
+ u_int64_t pad2[30];
+
+ /* 1fe.0000.0200,0210 */
+ struct iommureg psy_iommu;
+
+ u_int64_t pad3[317];
+
+ /* PCI bus a slot 0 irq map reg */ /* 1fe.0000.0c00 */
+ u_int64_t pcia0_int_map;
+ /* PCI bus a slot 1 irq map reg */ /* 1fe.0000.0c08 */
+ u_int64_t pcia1_int_map;
+ /* PCI bus a slot 2 irq map reg (IIi) */ /* 1fe.0000.0c10 */
+ u_int64_t pcia2_int_map;
+ /* PCI bus a slot 3 irq map reg (IIi) */ /* 1fe.0000.0c18 */
+ u_int64_t pcia3_int_map;
+ /* PCI bus b slot 0 irq map reg */ /* 1fe.0000.0c20 */
+ u_int64_t pcib0_int_map;
+ /* PCI bus b slot 1 irq map reg */ /* 1fe.0000.0c28 */
+ u_int64_t pcib1_int_map;
+ /* PCI bus b slot 2 irq map reg */ /* 1fe.0000.0c30 */
+ u_int64_t pcib2_int_map;
+ /* PCI bus b slot 3 irq map reg */ /* 1fe.0000.0c38 */
+ u_int64_t pcib3_int_map;
+
+ u_int64_t pad4[120];
+
+ /* SCSI interrupt map reg */ /* 1fe.0000.1000 */
+ u_int64_t scsi_int_map;
+ /* ethernet interrupt map reg */ /* 1fe.0000.1008 */
+ u_int64_t ether_int_map;
+ /* parallel interrupt map reg */ /* 1fe.0000.1010 */
+ u_int64_t bpp_int_map;
+ /* audio record interrupt map reg */ /* 1fe.0000.1018 */
+ u_int64_t audior_int_map;
+ /* audio playback interrupt map reg */ /* 1fe.0000.1020 */
+ u_int64_t audiop_int_map;
+ /* power fail interrupt map reg */ /* 1fe.0000.1028 */
+ u_int64_t power_int_map;
+ /* serial/kbd/mouse interrupt map reg */ /* 1fe.0000.1030 */
+ u_int64_t ser_kbd_ms_int_map;
+ /* floppy interrupt map reg */ /* 1fe.0000.1038 */
+ u_int64_t fd_int_map;
+ /* spare interrupt map reg */ /* 1fe.0000.1040 */
+ u_int64_t spare_int_map;
+ /* kbd [unused] interrupt map reg */ /* 1fe.0000.1048 */
+ u_int64_t kbd_int_map;
+ /* mouse [unused] interrupt map reg */ /* 1fe.0000.1050 */
+ u_int64_t mouse_int_map;
+ /* second serial interrupt map reg */ /* 1fe.0000.1058 */
+ u_int64_t serial_int_map;
+ /* timer 0 interrupt map reg */ /* 1fe.0000.1060 */
+ u_int64_t timer0_int_map;
+ /* timer 1 interrupt map reg */ /* 1fe.0000.1068 */
+ u_int64_t timer1_int_map;
+ /* UE interrupt map reg */ /* 1fe.0000.1070 */
+ u_int64_t ue_int_map;
+ /* CE interrupt map reg */ /* 1fe.0000.1078 */
+ u_int64_t ce_int_map;
+ /* PCI bus a error interrupt map reg */ /* 1fe.0000.1080 */
+ u_int64_t pciaerr_int_map;
+ /* PCI bus b error interrupt map reg */ /* 1fe.0000.1088 */
+ u_int64_t pciberr_int_map;
+ /* power mgmt wake interrupt map reg */ /* 1fe.0000.1090 */
+ u_int64_t pwrmgt_int_map;
+ /* FFB0 graphics interrupt map reg */ /* 1fe.0000.1098 */
+ u_int64_t ffb0_int_map;
+ /* FFB1 graphics interrupt map reg */ /* 1fe.0000.10a0 */
+ u_int64_t ffb1_int_map;
+
+ u_int64_t pad5[107];
+
+ /* Note: clear interrupt 0 registers are not really used */
+
+ /* PCI a slot 0 clear int regs 0..7 */ /* 1fe.0000.1400-1418 */
+ u_int64_t pcia0_int_clr[4];
+ /* PCI a slot 1 clear int regs 0..7 */ /* 1fe.0000.1420-1438 */
+ u_int64_t pcia1_int_clr[4];
+ /* PCI a slot 2 clear int regs 0..7 */ /* 1fe.0000.1440-1458 */
+ u_int64_t pcia2_int_clr[4];
+ /* PCI a slot 3 clear int regs 0..7 */ /* 1fe.0000.1480-1478 */
+ u_int64_t pcia3_int_clr[4];
+ /* PCI b slot 0 clear int regs 0..7 */ /* 1fe.0000.1480-1498 */
+ u_int64_t pcib0_int_clr[4];
+ /* PCI b slot 1 clear int regs 0..7 */ /* 1fe.0000.14a0-14b8 */
+ u_int64_t pcib1_int_clr[4];
+ /* PCI b slot 2 clear int regs 0..7 */ /* 1fe.0000.14c0-14d8 */
+ u_int64_t pcib2_int_clr[4];
+ /* PCI b slot 3 clear int regs 0..7 */ /* 1fe.0000.14d0-14f8 */
+ u_int64_t pcib3_int_clr[4];
+
+ u_int64_t pad6[96];
+
+ /* SCSI clear int reg */ /* 1fe.0000.1800 */
+ u_int64_t scsi_int_clr;
+ /* ethernet clear int reg */ /* 1fe.0000.1808 */
+ u_int64_t ether_int_clr;
+ /* parallel clear int reg */ /* 1fe.0000.1810 */
+ u_int64_t bpp_int_clr;
+ /* audio record clear int reg */ /* 1fe.0000.1818 */
+ u_int64_t audior_int_clr;
+ /* audio playback clear int reg */ /* 1fe.0000.1820 */
+ u_int64_t audiop_int_clr;
+ /* power fail clear int reg */ /* 1fe.0000.1828 */
+ u_int64_t power_int_clr;
+ /* serial/kbd/mouse clear int reg */ /* 1fe.0000.1830 */
+ u_int64_t ser_kb_ms_int_clr;
+ /* floppy clear int reg */ /* 1fe.0000.1838 */
+ u_int64_t fd_int_clr;
+ /* spare clear int reg */ /* 1fe.0000.1840 */
+ u_int64_t spare_int_clr;
+ /* kbd [unused] clear int reg */ /* 1fe.0000.1848 */
+ u_int64_t kbd_int_clr;
+ /* mouse [unused] clear int reg */ /* 1fe.0000.1850 */
+ u_int64_t mouse_int_clr;
+ /* second serial clear int reg */ /* 1fe.0000.1858 */
+ u_int64_t serial_clr;
+ /* timer 0 clear int reg */ /* 1fe.0000.1860 */
+ u_int64_t timer0_int_clr;
+ /* timer 1 clear int reg */ /* 1fe.0000.1868 */
+ u_int64_t timer1_int_clr;
+ /* UE clear int reg */ /* 1fe.0000.1870 */
+ u_int64_t ue_int_clr;
+ /* CE clear int reg */ /* 1fe.0000.1878 */
+ u_int64_t ce_int_clr;
+ /* PCI bus a error clear int reg */ /* 1fe.0000.1880 */
+ u_int64_t pciaerr_int_clr;
+ /* PCI bus b error clear int reg */ /* 1fe.0000.1888 */
+ u_int64_t pciberr_int_clr;
+ /* power mgmt wake clr interrupt reg */ /* 1fe.0000.1890 */
+ u_int64_t pwrmgt_int_clr;
+
+ u_int64_t pad7[45];
+
+ /* interrupt retry timer */ /* 1fe.0000.1a00 */
+ u_int64_t intr_retry_timer;
+
+ u_int64_t pad8[63];
+
+ struct timer_counter {
+ /* timer/counter 0/1 count register */ /* 1fe.0000.1c00,1c10 */
+ u_int64_t tc_count;
+ /* timer/counter 0/1 limit register */ /* 1fe.0000.1c08,1c18 */
+ u_int64_t tc_limit;
+ } tc[2];
+
+ /* PCI DMA write sync register (IIi) */ /* 1fe.0000.1c20 */
+ u_int64_t pci_dma_write_sync;
+
+ u_int64_t pad9[123];
+
+ struct pci_ctl {
+ /* PCI a/b control/status register */ /* 1fe.0000.2000,4000 */
+ u_int64_t pci_csr;
+ u_int64_t pad10;
+ /* PCI a/b AFSR register */ /* 1fe.0000.2010,4010 */
+ u_int64_t pci_afsr;
+ /* PCI a/b AFAR register */ /* 1fe.0000.2018,4018 */
+ u_int64_t pci_afar;
+ /* PCI a/b diagnostic register */ /* 1fe.0000.2020,4020 */
+ u_int64_t pci_diag;
+ /* PCI target address space reg (IIi)*/ /* 1fe.0000.2028,4028 */
+ u_int64_t pci_tasr;
+
+ u_int64_t pad11[250];
+
+ /* This is really the IOMMU's, not the PCI bus's */
+ /* 1fe.0000.2800-210 */
+ struct iommu_strbuf pci_strbuf;
+#define psy_iommu_strbuf psy_pcictl[0].pci_strbuf
+
+ u_int64_t pad12[765];
+ } psy_pcictl[2]; /* For PCI a and b */
+
+ /*
+ * NB: FFB0 and FFB1 intr map regs also appear at 1fe.0000.6000 and
+ * 1fe.0000.8000 respectively
+ */
+ u_int64_t pad13[2048];
+
+ /* DMA scoreboard diag reg 0 */ /* 1fe.0000.a000 */
+ u_int64_t dma_scb_diag0;
+ /* DMA scoreboard diag reg 1 */ /* 1fe.0000.a008 */
+ u_int64_t dma_scb_diag1;
+
+ u_int64_t pad14[126];
+
+ /* IOMMU virtual addr diag reg */ /* 1fe.0000.a400 */
+ u_int64_t iommu_svadiag;
+ /* IOMMU TLB tag compare diag reg */ /* 1fe.0000.a408 */
+ u_int64_t iommu_tlb_comp_diag;
+
+ u_int64_t pad15[30];
+
+ /* IOMMU LRU queue diag */ /* 1fe.0000.a500-a578 */
+ u_int64_t iommu_queue_diag[16];
+ /* TLB tag diag */ /* 1fe.0000.a580-a5f8 */
+ u_int64_t tlb_tag_diag[16];
+ /* TLB data RAM diag */ /* 1fe.0000.a600-a678 */
+ u_int64_t tlb_data_diag[16];
+
+ u_int64_t pad16[48];
+
+ /* PCI int state diag reg */ /* 1fe.0000.a800 */
+ u_int64_t pci_int_diag;
+ /* OBIO and misc int state diag reg */ /* 1fe.0000.a808 */
+ u_int64_t obio_int_diag;
+
+ u_int64_t pad17[254];
+
+ struct strbuf_diag {
+ /* streaming buffer data RAM diag */ /* 1fe.0000.b000-b3f8 */
+ u_int64_t strbuf_data_diag[128];
+ /* streaming buffer error status diag *//* 1fe.0000.b400-b7f8 */
+ u_int64_t strbuf_error_diag[128];
+ /* streaming buffer page tag diag */ /* 1fe.0000.b800-b878 */
+ u_int64_t strbuf_pg_tag_diag[16];
+ u_int64_t pad18[16];
+ /* streaming buffer line tag diag */ /* 1fe.0000.b900-b978 */
+ u_int64_t strbuf_ln_tag_diag[16];
+ u_int64_t pad19[208];
+ } psy_strbufdiag[2]; /* For PCI a and b */
+
+ /*
+ * Here is the rest of the map, which we're not specifying:
+ *
+ * 1fe.0100.0000 - 1fe.01ff.ffff PCI configuration space
+ * 1fe.0100.0000 - 1fe.0100.00ff PCI B configuration header
+ * 1fe.0101.0000 - 1fe.0101.00ff PCI A configuration header
+ * 1fe.0200.0000 - 1fe.0200.ffff PCI A I/O space
+ * 1fe.0201.0000 - 1fe.0201.ffff PCI B I/O space
+ * 1ff.0000.0000 - 1ff.7fff.ffff PCI A memory space
+ * 1ff.8000.0000 - 1ff.ffff.ffff PCI B memory space
+ *
+ * NB: config and I/O space can use 1-4 byte accesses, not 8 byte
+ * accesses. Memory space can use any sized accesses.
+ *
+ * Note that the SUNW,sabre/SUNW,simba combinations found on the
+ * Ultra5 and Ultra10 machines uses slightly differrent addresses
+ * than the above. This is mostly due to the fact that the APB is
+ * a multi-function PCI device with two PCI bridges, and the U2P is
+ * two separate PCI bridges. It uses the same PCI configuration
+ * space, though the configuration header for each PCI bus is
+ * located differently due to the SUNW,simba PCI busses being
+ * function 0 and function 1 of the APB, whereas the psycho's are
+ * each their own PCI device. The I/O and memory spaces are each
+ * split into 8 equally sized areas (8x2MB blocks for I/O space,
+ * and 8x512MB blocks for memory space). These are allocated in to
+ * either PCI A or PCI B, or neither in the APB's `I/O Address Map
+ * Register A/B' (0xde) and `Memory Address Map Register A/B' (0xdf)
+ * registers of each simba. We must ensure that both of the
+ * following are correct (the prom should do this for us):
+ *
+ * (PCI A Memory Address Map) & (PCI B Memory Address Map) == 0
+ *
+ * (PCI A I/O Address Map) & (PCI B I/O Address Map) == 0
+ *
+ * 1fe.0100.0000 - 1fe.01ff.ffff PCI configuration space
+ * 1fe.0100.0800 - 1fe.0100.08ff PCI B configuration header
+ * 1fe.0100.0900 - 1fe.0100.09ff PCI A configuration header
+ * 1fe.0200.0000 - 1fe.02ff.ffff PCI I/O space (divided)
+ * 1ff.0000.0000 - 1ff.ffff.ffff PCI memory space (divided)
+ */
+};
+
+#define PSYCHO_CONF_SIZE 0x1000000
+#define PSYCHO_CONF_BUS_SHIFT 16
+#define PSYCHO_CONF_DEV_SHIFT 11
+#define PSYCHO_CONF_FUNC_SHIFT 8
+#define PSYCHO_CONF_REG_SHIFT 0
+#define PSYCHO_IO_SIZE 0x1000000
+#define PSYCHO_MEM_SIZE 0x100000000
+
+#define PSYCHO_CONF_OFF(bus, slot, func, reg) \
+ (((bus) << PSYCHO_CONF_BUS_SHIFT) | \
+ ((slot) << PSYCHO_CONF_DEV_SHIFT) | \
+ ((func) << PSYCHO_CONF_FUNC_SHIFT) | \
+ ((reg) << PSYCHO_CONF_REG_SHIFT))
+
+/* what the bits mean! */
+
+/* PCI [a|b] control/status register */
+/* note that the sabre only has one set of PCI control/status registers */
+#define PCICTL_MRLM 0x0000001000000000 /* Memory Read Line/Multiple */
+#define PCICTL_SERR 0x0000000400000000 /* SERR asserted; W1C */
+#define PCICTL_ARB_PARK 0x0000000000200000 /* PCI arbitration parking */
+#define PCICTL_CPU_PRIO 0x0000000000100000 /* PCI arbitration parking */
+#define PCICTL_ARB_PRIO 0x00000000000f0000 /* PCI arbitration parking */
+#define PCICTL_ERRINTEN 0x0000000000000100 /* PCI error interrupt enable */
+#define PCICTL_RTRYWAIT 0x0000000000000080 /* PCI error interrupt enable */
+#define PCICTL_4ENABLE 0x000000000000000f /* enable 4 PCI slots */
+#define PCICTL_6ENABLE 0x000000000000003f /* enable 6 PCI slots */
+
+/*
+ * these are the PROM structures we grovel
+ */
+
+/*
+ * For the physical adddresses split into 3 32 bit values, we deocde
+ * them like the following (IEEE1275 PCI Bus binding 2.0, 2.2.1.1
+ * Numerical Representation):
+ *
+ * phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr
+ * phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
+ * phys.lo cell: llllllll llllllll llllllll llllllll
+ *
+ * where these bits affect the address' properties:
+ * n not-relocatable
+ * p prefetchable
+ * t aliased (non-relocatable IO), below 1MB (memory) or
+ * below 64KB (reloc. IO)
+ * ss address space code:
+ * 00 - configuration space
+ * 01 - I/O space
+ * 10 - 32 bit memory space
+ * 11 - 64 bit memory space
+ * bb..bb 8 bit bus number
+ * ddddd 5 bit device number
+ * fff 3 bit function number
+ * rr..rr 8 bit register number
+ * hh..hh 32 bit unsigned value
+ * ll..ll 32 bit unsigned value
+ * the values of hh..hh and ll..ll are combined to form a larger number.
+ *
+ * For config space, we don't have to do much special. For I/O space,
+ * hh..hh must be zero, and if n == 0 ll..ll is the offset from the
+ * start of I/O space, otherwise ll..ll is the I/O space. For memory
+ * space, hh..hh must be zero for the 32 bit space, and is the high 32
+ * bits in 64 bit space, with ll..ll being the low 32 bits in both cases,
+ * with offset handling being driver via `n == 0' as for I/O space.
+ */
+
+/* commonly used */
+#define TAG2BUS(tag) ((tag) >> 16) & 0xff;
+#define TAG2DEV(tag) ((tag) >> 11) & 0x1f;
+#define TAG2FN(tag) ((tag) >> 8) & 0x7;
+
+#define INTPCI_MAXOBINO 0x16 /* maximum OBIO INO value for PCI */
+#define INTPCIOBINOX(x) ((x) & 0x1f) /* OBIO ino index (for PCI machines) */
+#define INTPCIINOX(x) (((x) & 0x1c) >> 2) /* PCI ino index */
+
+#endif /* _SPARC64_PCI_PSYCHOREG_H_ */
diff --git a/sys/sparc64/pci/psychovar.h b/sys/sparc64/pci/psychovar.h
new file mode 100644
index 0000000..790f361
--- /dev/null
+++ b/sys/sparc64/pci/psychovar.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1999, 2000 Matthew R. Green
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 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: NetBSD: psychovar.h,v 1.6 2001/07/20 00:07:13 eeh Exp
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SPARC64_PCI_PSYCHOVAR_H_
+#define _SPARC64_PCI_PSYCHOVAR_H_
+
+/*
+ * per-PCI bus on mainbus softc structure; one for sabre, or two
+ * per pair of psycho's.
+ */
+struct psycho_softc {
+ device_t sc_dev;
+ /*
+ * PSYCHO register. we record the base physical address of these
+ * also as it is the base of the entire PSYCHO
+ */
+ struct psychoreg *sc_regs;
+ vm_offset_t sc_basepaddr;
+
+ /* Interrupt Group Number for this device */
+ int sc_ign;
+
+ /* our tags (from parent) */
+ bus_space_tag_t sc_bustag;
+ bus_dma_tag_t sc_dmatag;
+
+ int sc_clockfreq;
+ phandle_t sc_node; /* prom node */
+ int sc_mode;
+#define PSYCHO_MODE_SABRE 1
+#define PSYCHO_MODE_PSYCHO 2
+
+ struct iommu_state *sc_is;
+
+ struct resource *sc_irq[6];
+ void *sc_ihand[6];
+
+ /*
+ * note that the sabre really only has one ranges property,
+ * used for both simba a and simba b (but the ranges for
+ * real psychos are the same for PCI A and PCI B anyway).
+ */
+ struct upa_ranges *sc_range;
+ int sc_nrange;
+ struct ofw_pci_imap *sc_intrmap;
+ int sc_nintrmap;
+ struct ofw_pci_imap_msk sc_intrmapmsk;
+
+ /* our tags */
+ bus_space_tag_t sc_cfgt;
+ bus_space_tag_t sc_memt;
+ bus_space_tag_t sc_iot;
+ bus_dma_tag_t sc_dmat;
+
+ bus_space_handle_t sc_bh[4];
+
+ int sc_busno;
+
+ struct rman sc_mem_rman;
+ struct rman sc_io_rman;
+};
+
+#endif /* _SPARC64_PCI_PSYCHOVAR_H_ */
OpenPOWER on IntegriCloud