diff options
author | tmm <tmm@FreeBSD.org> | 2001-11-09 20:19:58 +0000 |
---|---|---|
committer | tmm <tmm@FreeBSD.org> | 2001-11-09 20:19:58 +0000 |
commit | d9d3e67ce7bd0548d288122b0e6b2af477a6fc14 (patch) | |
tree | 8a0741ca1fda692aae84db81a64fc0b73bb5dfec /sys/sparc64/pci | |
parent | 904109a4f9c52cc69f2df3836e4b3b9d4c2bf99d (diff) | |
download | FreeBSD-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.
Diffstat (limited to 'sys/sparc64/pci')
-rw-r--r-- | sys/sparc64/pci/apb.c | 303 | ||||
-rw-r--r-- | sys/sparc64/pci/ofw_pci.c | 267 | ||||
-rw-r--r-- | sys/sparc64/pci/ofw_pci.h | 73 | ||||
-rw-r--r-- | sys/sparc64/pci/psycho.c | 1210 | ||||
-rw-r--r-- | sys/sparc64/pci/psychoreg.h | 437 | ||||
-rw-r--r-- | sys/sparc64/pci/psychovar.h | 92 |
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_ */ |