diff options
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/sparc64/dev/psycho.c | 1126 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/psychoreg.h | 354 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/psychovar.h | 119 |
3 files changed, 1599 insertions, 0 deletions
diff --git a/sys/arch/sparc64/dev/psycho.c b/sys/arch/sparc64/dev/psycho.c new file mode 100644 index 00000000000..dbbfaeaa5c4 --- /dev/null +++ b/sys/arch/sparc64/dev/psycho.c @@ -0,0 +1,1126 @@ +/* $NetBSD: psycho.c,v 1.34 2001/07/20 00:07:13 eeh Exp $ */ + +/* + * 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. + */ + +/* + * Support for `psycho' and `psycho+' UPA to PCI bridge and + * UltraSPARC IIi and IIe `sabre' PCI controllers. + */ + +#ifdef DEBUG +#define PDB_PROM 0x01 +#define PDB_BUSMAP 0x02 +#define PDB_INTR 0x04 +int psycho_debug = 0x0; +#define DPRINTF(l, s) do { if (psycho_debug & l) printf s; } while (0) +#else +#define DPRINTF(l, s) +#endif + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/errno.h> +#include <sys/extent.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/reboot.h> + +#define _SPARC_BUS_DMA_PRIVATE +#include <machine/bus.h> +#include <machine/autoconf.h> +#include <machine/psl.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include <sparc64/dev/iommureg.h> +#include <sparc64/dev/iommuvar.h> +#include <sparc64/dev/psychoreg.h> +#include <sparc64/dev/psychovar.h> +#include <sparc64/sparc64/cache.h> + +static pci_chipset_tag_t psycho_alloc_chipset __P((struct psycho_pbm *, int, + pci_chipset_tag_t)); +static void psycho_get_bus_range __P((int, int *)); +static void psycho_get_ranges __P((int, struct psycho_ranges **, int *)); +static void psycho_set_intr __P((struct psycho_softc *, int, void *, + u_int64_t *, u_int64_t *)); + +/* Interrupt handlers */ +static int psycho_ue __P((void *)); +static int psycho_ce __P((void *)); +static int psycho_bus_a __P((void *)); +static int psycho_bus_b __P((void *)); +static int psycho_powerfail __P((void *)); +static int psycho_wakeup __P((void *)); + + +/* IOMMU support */ +static void psycho_iommu_init __P((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_bus_mmap __P((bus_space_tag_t, bus_type_t, bus_addr_t, + int, bus_space_handle_t *)); +static int _psycho_bus_map __P((bus_space_tag_t, bus_type_t, bus_addr_t, + bus_size_t, int, vaddr_t, + bus_space_handle_t *)); +static void *psycho_intr_establish __P((bus_space_tag_t, int, int, int, + int (*) __P((void *)), void *)); + +static int psycho_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *, + bus_size_t, struct proc *, int)); +static void psycho_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t)); +static int psycho_dmamap_load_raw __P((bus_dma_tag_t, bus_dmamap_t, + bus_dma_segment_t *, int, bus_size_t, int)); +static void psycho_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t, bus_addr_t, + bus_size_t, int)); +int psycho_dmamem_alloc __P((bus_dma_tag_t, bus_size_t, bus_size_t, bus_size_t, + bus_dma_segment_t *, int, int *, int)); +void psycho_dmamem_free __P((bus_dma_tag_t, bus_dma_segment_t *, int)); +int psycho_dmamem_map __P((bus_dma_tag_t, bus_dma_segment_t *, int, size_t, + caddr_t *, int)); +void psycho_dmamem_unmap __P((bus_dma_tag_t, caddr_t, size_t)); + +/* base pci_chipset */ +extern struct sparc_pci_chipset _sparc_pci_chipset; + +/* + * autoconfiguration + */ +static int psycho_match __P((struct device *, void *, void *)); +static void psycho_attach __P((struct device *, struct device *, void *)); +static int psycho_print __P((void *aux, const char *p)); + +struct cfattach psycho_ca = { + sizeof(struct psycho_softc), psycho_match, psycho_attach +}; + +struct cfdriver psycho_cd = { + NULL, "psycho", DV_DULL +}; + +/* + * "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 ROM_PCI_NAME "pci" +#define ROM_SABRE_MODEL "SUNW,sabre" +#define ROM_SIMBA_MODEL "SUNW,simba" +#define ROM_PSYCHO_MODEL "SUNW,psycho" + +static int +psycho_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct mainbus_attach_args *ma = aux; + char *model = getpropstring(ma->ma_node, "model"); + + /* match on a name of "pci" and a sabre or a psycho */ + if (strcmp(ma->ma_name, ROM_PCI_NAME) == 0 && + (strcmp(model, ROM_SABRE_MODEL) == 0 || + strcmp(model, ROM_PSYCHO_MODEL) == 0)) + return (1); + + return (0); +} + +/* + * SUNW,psycho initialisation .. + * - find the per-psycho registers + * - figure out the IGN. + * - find our partner psycho + * - configure ourselves + * - bus range, bus, + * - get interrupt-map and interrupt-map-mask + * - setup the chipsets. + * - if we're the first of the pair, initialise the IOMMU, otherwise + * just copy it's tags and addresses. + */ +static void +psycho_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct psycho_softc *sc = (struct psycho_softc *)self; + struct psycho_softc *osc = NULL; + struct psycho_pbm *pp; + struct pcibus_attach_args pba; + struct mainbus_attach_args *ma = aux; + bus_space_handle_t bh; + u_int64_t csr; + int psycho_br[2], n; + struct pci_ctl *pci_ctl; + char *model = getpropstring(ma->ma_node, "model"); + + printf("\n"); + + sc->sc_node = ma->ma_node; + sc->sc_bustag = ma->ma_bustag; + sc->sc_dmatag = ma->ma_dmatag; + + /* + * call the model-specific initialisation routine. + */ + + if (strcmp(model, ROM_SABRE_MODEL) == 0) + sc->sc_mode = PSYCHO_MODE_SABRE; + else if (strcmp(model, ROM_PSYCHO_MODEL) == 0) + sc->sc_mode = PSYCHO_MODE_PSYCHO; + else + panic("psycho_attach: unknown model %s?", 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. + */ + + /* Register layouts are different. stuupid. */ + if (sc->sc_mode == PSYCHO_MODE_PSYCHO) { + sc->sc_basepaddr = (paddr_t)ma->ma_reg[2].ur_paddr; + + if (ma->ma_naddress > 2) { + sc->sc_regs = (struct psychoreg *) + (u_long)ma->ma_address[2]; + pci_ctl = (struct pci_ctl *) + (u_long)ma->ma_address[0]; + } else if (ma->ma_nreg > 2) { + bus_space_handle_t handle; + + /* We need to map this in ourselves. */ + if (bus_space_map2(sc->sc_bustag, 0, + ma->ma_reg[2].ur_paddr, + ma->ma_reg[2].ur_len, 0, NULL, &handle)) + panic("psycho_attach: cannot map regs"); + sc->sc_regs = (struct psychoreg *)(u_long)handle; + + if (bus_space_map2(sc->sc_bustag, 0, + ma->ma_reg[0].ur_paddr, + ma->ma_reg[0].ur_len, 0, NULL, &handle)) + panic("psycho_attach: cannot map ctl"); +/* XXX -- this is lost but never unmapped */ + pci_ctl = (struct pci_ctl *)(u_long)handle; + + } else + panic("psycho_attach: %d not enough registers", + ma->ma_nreg); + } else { + sc->sc_basepaddr = (paddr_t)ma->ma_reg[0].ur_paddr; + + if (ma->ma_naddress) { + sc->sc_regs = (struct psychoreg *) + (u_long)ma->ma_address[0]; + pci_ctl = (struct pci_ctl *)&sc->sc_regs->psy_pcictl[0]; + } else if (ma->ma_nreg) { + bus_space_handle_t handle; + + /* We need to map this in ourselves. */ + if (bus_space_map2(sc->sc_bustag, 0, + ma->ma_reg[0].ur_paddr, + ma->ma_reg[0].ur_len, 0, NULL, &handle)) + panic("psycho_attach: cannot map regs"); + sc->sc_regs = (struct psychoreg *)(u_long)handle; + pci_ctl = (struct pci_ctl *)&sc->sc_regs->psy_pcictl[0]; + } else + panic("psycho_attach: %d not enough registers", + ma->ma_nreg); + } + + 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; + + printf("%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_cd.cd_ndevs; n++) { + + struct psycho_softc *asc = + (struct psycho_softc *)psycho_cd.cd_devs[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; + } + + + /* Oh, dear. OK, lets get started */ + + /* + * Setup the PCI control register + */ + csr = bus_space_read_8(sc->sc_bustag, + (bus_space_handle_t)(u_long)&pci_ctl->pci_csr, 0); + 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)(u_long)&pci_ctl->pci_csr, 0, csr); + + + /* + * Allocate our psycho_pbm + */ + pp = sc->sc_psycho_this = malloc(sizeof *pp, M_DEVBUF, M_NOWAIT); + if (pp == NULL) + panic("could not allocate psycho pbm"); + + memset(pp, 0, sizeof *pp); + + pp->pp_sc = sc; + + /* grab the psycho ranges */ + psycho_get_ranges(sc->sc_node, &pp->pp_range, &pp->pp_nrange); + + /* get the bus-range for the psycho */ + psycho_get_bus_range(sc->sc_node, psycho_br); + + pba.pba_bus = psycho_br[0]; + + printf("bus range %u to %u", psycho_br[0], psycho_br[1]); + printf("; PCI bus %d", psycho_br[0]); + + pp->pp_pcictl = &sc->sc_regs->psy_pcictl[0]; + + /* allocate our tags */ + pp->pp_memt = psycho_alloc_mem_tag(pp); + pp->pp_iot = psycho_alloc_io_tag(pp); + pp->pp_dmat = psycho_alloc_dma_tag(pp); + pp->pp_flags = (pp->pp_memt ? PCI_FLAGS_MEM_ENABLED : 0) | + (pp->pp_iot ? PCI_FLAGS_IO_ENABLED : 0); + + /* allocate a chipset for this */ + pp->pp_pc = psycho_alloc_chipset(pp, sc->sc_node, &_sparc_pci_chipset); + + /* setup the rest of the psycho pbm */ + pba.pba_pc = psycho_alloc_chipset(pp, sc->sc_node, pp->pp_pc); + + printf("\n"); + + /* + * 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, 15, psycho_ue, + &sc->sc_regs->ue_int_map, + &sc->sc_regs->ue_clr_int); + psycho_set_intr(sc, 1, psycho_ce, + &sc->sc_regs->ce_int_map, + &sc->sc_regs->ce_clr_int); + psycho_set_intr(sc, 15, psycho_bus_a, + &sc->sc_regs->pciaerr_int_map, + &sc->sc_regs->pciaerr_clr_int); + psycho_set_intr(sc, 15, psycho_bus_b, + &sc->sc_regs->pciberr_int_map, + &sc->sc_regs->pciberr_clr_int); + psycho_set_intr(sc, 15, psycho_powerfail, + &sc->sc_regs->power_int_map, + &sc->sc_regs->power_clr_int); + psycho_set_intr(sc, 1, psycho_wakeup, + &sc->sc_regs->pwrmgt_int_map, + &sc->sc_regs->pwrmgt_clr_int); + + /* + * 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); + + sc->sc_configtag = psycho_alloc_config_tag(sc->sc_psycho_this); + if (bus_space_map2(sc->sc_bustag, + PCI_CONFIG_BUS_SPACE, + sc->sc_basepaddr + 0x01000000, + 0x0100000, + 0, + 0, + &bh)) + panic("could not map psycho PCI configuration space"); + sc->sc_configaddr = (off_t)bh; + } else { + /* Just copy IOMMU state, config tag and address */ + sc->sc_is = osc->sc_is; + sc->sc_configtag = osc->sc_configtag; + sc->sc_configaddr = osc->sc_configaddr; + } + + /* + * attach the pci.. note we pass PCI A tags, etc., for the sabre here. + */ + pba.pba_busname = "pci"; +#if 0 + pba.pba_flags = sc->sc_psycho_this->pp_flags; +#endif + pba.pba_dmat = sc->sc_psycho_this->pp_dmat; + pba.pba_iot = sc->sc_psycho_this->pp_iot; + pba.pba_memt = sc->sc_psycho_this->pp_memt; + + config_found(self, &pba, psycho_print); +} + +static int +psycho_print(aux, p) + void *aux; + const char *p; +{ + + if (p == NULL) + return (UNCONF); + return (QUIET); +} + +static void +psycho_set_intr(sc, ipl, handler, mapper, clearer) + struct psycho_softc *sc; + int ipl; + void *handler; + u_int64_t *mapper; + u_int64_t *clearer; +{ + struct intrhand *ih; + + ih = (struct intrhand *)malloc(sizeof(struct intrhand), + M_DEVBUF, M_NOWAIT); + ih->ih_arg = sc; + ih->ih_map = mapper; + ih->ih_clr = clearer; + ih->ih_fun = handler; + ih->ih_pil = (1<<ipl); + ih->ih_number = INTVEC(*(ih->ih_map)); + intr_establish(ipl, ih); + *(ih->ih_map) |= INTMAP_V; +} + +/* + * PCI bus support + */ + +/* + * allocate a PCI chipset tag and set it's cookie. + */ +static pci_chipset_tag_t +psycho_alloc_chipset(pp, node, pc) + struct psycho_pbm *pp; + int node; + pci_chipset_tag_t pc; +{ + pci_chipset_tag_t npc; + + npc = malloc(sizeof *npc, M_DEVBUF, M_NOWAIT); + if (npc == NULL) + panic("could not allocate pci_chipset_tag_t"); + memcpy(npc, pc, sizeof *pc); + npc->cookie = pp; + npc->rootnode = node; + npc->curnode = node; + + return (npc); +} + +/* + * grovel the OBP for various psycho properties + */ +static void +psycho_get_bus_range(node, brp) + int node; + int *brp; +{ + int n; + + if (getprop(node, "bus-range", sizeof(*brp), &n, (void **)&brp)) + panic("could not get psycho bus-range"); + if (n != 2) + panic("broken psycho bus-range"); + DPRINTF(PDB_PROM, ("psycho debug: got `bus-range' for node %08x: %u - %u\n", node, brp[0], brp[1])); +} + +static void +psycho_get_ranges(node, rp, np) + int node; + struct psycho_ranges **rp; + int *np; +{ + + if (getprop(node, "ranges", sizeof(**rp), np, (void **)rp)) + panic("could not get psycho ranges"); + DPRINTF(PDB_PROM, ("psycho debug: got `ranges' for node %08x: %d entries\n", node, *np)); +} + +/* + * Interrupt handlers. + */ + +static int +psycho_ue(arg) + void *arg; +{ + struct psycho_softc *sc = (struct psycho_softc *)arg; + struct psychoreg *regs = sc->sc_regs; + + /* + * It's uncorrectable. Dump the regs and panic. + */ + + panic("%s: uncorrectable DMA error AFAR %llx AFSR %llx\n", + sc->sc_dev.dv_xname, + (long long)regs->psy_ue_afar, (long long)regs->psy_ue_afsr); + return (1); +} +static int +psycho_ce(arg) + void *arg; +{ + struct psycho_softc *sc = (struct psycho_softc *)arg; + struct psychoreg *regs = sc->sc_regs; + + /* + * It's correctable. Dump the regs and continue. + */ + + printf("%s: correctable DMA error AFAR %llx AFSR %llx\n", + sc->sc_dev.dv_xname, + (long long)regs->psy_ce_afar, (long long)regs->psy_ce_afsr); + return (1); +} +static int +psycho_bus_a(arg) + void *arg; +{ + struct psycho_softc *sc = (struct psycho_softc *)arg; + struct psychoreg *regs = sc->sc_regs; + + /* + * It's uncorrectable. Dump the regs and panic. + */ + + panic("%s: PCI bus A error AFAR %llx AFSR %llx\n", + sc->sc_dev.dv_xname, + (long long)regs->psy_ue_afar, (long long)regs->psy_ue_afsr); + return (1); +} +static int +psycho_bus_b(arg) + void *arg; +{ + struct psycho_softc *sc = (struct psycho_softc *)arg; + struct psychoreg *regs = sc->sc_regs; + + /* + * It's uncorrectable. Dump the regs and panic. + */ + + panic("%s: PCI bus B error AFAR %llx AFSR %llx\n", + sc->sc_dev.dv_xname, + (long long)regs->psy_ue_afar, (long long)regs->psy_ue_afsr); + return (1); +} +static int +psycho_powerfail(arg) + void *arg; +{ + + /* + * We lost power. Try to shut down NOW. + */ + printf("Power Failure Detected: Shutting down NOW.\n"); + boot(RB_POWERDOWN|RB_HALT); + return (1); +} +static +int psycho_wakeup(arg) + void *arg; +{ + struct psycho_softc *sc = (struct psycho_softc *)arg; + + /* + * Gee, we don't really have a framework to deal with this + * properly. + */ + printf("%s: power management wakeup\n", sc->sc_dev.dv_xname); + return (1); +} + + + +/* + * initialise the IOMMU.. + */ +void +psycho_iommu_init(sc, tsbsize) + 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 is"); + + sc->sc_is = is; + + /* punch in our copies */ + is->is_bustag = sc->sc_bustag; + is->is_iommu = &sc->sc_regs->psy_iommu; + + if (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. + */ + if (!getprop(sc->sc_node, "virtual-dma", sizeof(vdma), &nitem, + (void **)&vdma)) { + /* Damn. Gotta use these values. */ + iobase = vdma[0]; +#define TSBCASE(x) case 1<<((x)+23): tsbsize = (x); break + switch (vdma[1]) { + TSBCASE(1); TSBCASE(2); TSBCASE(3); + TSBCASE(4); TSBCASE(5); TSBCASE(6); + default: + printf("bogus tsb size %x, using 7\n", vdma[1]); + TSBCASE(7); + } +#undef TSBCASE + } + + /* 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", sc->sc_dev.dv_xname); + + iommu_init(name, is, tsbsize, iobase); +} + +/* + * below here is bus space and bus dma support + */ +bus_space_tag_t +psycho_alloc_bus_tag(pp, type) + struct psycho_pbm *pp; + int type; +{ + struct psycho_softc *sc = pp->pp_sc; + bus_space_tag_t bt; + + bt = (bus_space_tag_t) + malloc(sizeof(struct sparc_bus_space_tag), M_DEVBUF, M_NOWAIT); + if (bt == NULL) + panic("could not allocate psycho bus tag"); + + bzero(bt, sizeof *bt); + bt->cookie = pp; + bt->parent = sc->sc_bustag; + bt->type = type; + bt->sparc_bus_map = _psycho_bus_map; + bt->sparc_bus_mmap = psycho_bus_mmap; + bt->sparc_intr_establish = psycho_intr_establish; + return (bt); +} + +bus_dma_tag_t +psycho_alloc_dma_tag(pp) + struct psycho_pbm *pp; +{ + struct psycho_softc *sc = pp->pp_sc; + bus_dma_tag_t dt, pdt = sc->sc_dmatag; + + dt = (bus_dma_tag_t) + malloc(sizeof(struct sparc_bus_dma_tag), M_DEVBUF, M_NOWAIT); + if (dt == NULL) + panic("could not allocate psycho dma tag"); + + bzero(dt, sizeof *dt); + dt->_cookie = pp; + dt->_parent = pdt; +#define PCOPY(x) dt->x = pdt->x + PCOPY(_dmamap_create); + PCOPY(_dmamap_destroy); + dt->_dmamap_load = psycho_dmamap_load; + PCOPY(_dmamap_load_mbuf); + PCOPY(_dmamap_load_uio); + dt->_dmamap_load_raw = psycho_dmamap_load_raw; + dt->_dmamap_unload = psycho_dmamap_unload; + dt->_dmamap_sync = psycho_dmamap_sync; + dt->_dmamem_alloc = psycho_dmamem_alloc; + dt->_dmamem_free = psycho_dmamem_free; + dt->_dmamem_map = psycho_dmamem_map; + dt->_dmamem_unmap = psycho_dmamem_unmap; + PCOPY(_dmamem_mmap); +#undef PCOPY + return (dt); +} + +/* + * bus space support. <sparc64/dev/psychoreg.h> has a discussion about + * PCI physical addresses. + */ + +static int get_childspace __P((int)); + +static int +get_childspace(type) + int type; +{ + int ss; + + switch (type) { + case PCI_CONFIG_BUS_SPACE: + ss = 0x00; + break; + case PCI_IO_BUS_SPACE: + ss = 0x01; + break; + case PCI_MEMORY_BUS_SPACE: + ss = 0x02; + break; +#if 0 + /* we don't do 64 bit memory space */ + case PCI_MEMORY64_BUS_SPACE: + ss = 0x03; + break; +#endif + default: + panic("get_childspace: unknown bus type"); + } + + return (ss); +} + +static int +_psycho_bus_map(t, btype, offset, size, flags, vaddr, hp) + bus_space_tag_t t; + bus_type_t btype; + bus_addr_t offset; + bus_size_t size; + int flags; + vaddr_t vaddr; + bus_space_handle_t *hp; +{ + struct psycho_pbm *pp = t->cookie; + struct psycho_softc *sc = pp->pp_sc; + int i, ss; + + DPRINTF(PDB_BUSMAP, ("_psycho_bus_map: type %d off %qx sz %qx flags %d va %p", t->type, (unsigned long long)offset, (unsigned long long)size, flags, + (void *)vaddr)); + + ss = get_childspace(t->type); + DPRINTF(PDB_BUSMAP, (" cspace %d", ss)); + + for (i = 0; i < pp->pp_nrange; i++) { + bus_addr_t paddr; + + if (((pp->pp_range[i].cspace >> 24) & 0x03) != ss) + continue; + + paddr = pp->pp_range[i].phys_lo + offset; + paddr |= ((bus_addr_t)pp->pp_range[i].phys_hi<<32); + DPRINTF(PDB_BUSMAP, ("\n_psycho_bus_map: mapping paddr space %lx offset %lx paddr %qx\n", + (long)ss, (long)offset, + (unsigned long long)paddr)); + return (bus_space_map2(sc->sc_bustag, t->type, paddr, + size, flags, vaddr, hp)); + } + DPRINTF(PDB_BUSMAP, (" FAILED\n")); + return (EINVAL); +} + +static int +psycho_bus_mmap(t, btype, paddr, flags, hp) + bus_space_tag_t t; + bus_type_t btype; + bus_addr_t paddr; + int flags; + bus_space_handle_t *hp; +{ + bus_addr_t offset = paddr; + struct psycho_pbm *pp = t->cookie; + struct psycho_softc *sc = pp->pp_sc; + int i, ss; + + ss = get_childspace(t->type); + + DPRINTF(PDB_BUSMAP, ("_psycho_bus_mmap: type %d flags %d pa %qx\n", btype, flags, (unsigned long long)paddr)); + + for (i = 0; i < pp->pp_nrange; i++) { + bus_addr_t paddr; + + if (((pp->pp_range[i].cspace >> 24) & 0x03) != ss) + continue; + + paddr = pp->pp_range[i].phys_lo + offset; + paddr |= ((bus_addr_t)pp->pp_range[i].phys_hi<<32); + DPRINTF(PDB_BUSMAP, ("\n_psycho_bus_mmap: mapping paddr space %lx offset %lx paddr %qx\n", + (long)ss, (long)offset, + (unsigned long long)paddr)); + return (bus_space_mmap(sc->sc_bustag, 0, paddr, + flags, hp)); + } + + return (-1); +} + + +/* + * install an interrupt handler for a PCI device + */ +void * +psycho_intr_establish(t, ihandle, level, flags, handler, arg) + bus_space_tag_t t; + int ihandle; + int level; + int flags; + int (*handler) __P((void *)); + void *arg; +{ + struct psycho_pbm *pp = t->cookie; + struct psycho_softc *sc = pp->pp_sc; + struct intrhand *ih; + volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL; + int64_t intrmap = 0; + int ino; + long vec = INTVEC(ihandle); + + ih = (struct intrhand *) + malloc(sizeof(struct intrhand), M_DEVBUF, M_NOWAIT); + if (ih == 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. + */ + + + DPRINTF(PDB_INTR, ("\npsycho_intr_establish: ihandle %x vec %lx", ihandle, vec)); + ino = INTINO(vec); + DPRINTF(PDB_INTR, (" ino %x", ino)); + + /* If the device didn't ask for an IPL, use the one encoded. */ + if (level == IPL_NONE) level = INTLEV(vec); + /* If it still has no level, print a warning and assign IPL 2 */ + if (level == IPL_NONE) { + printf("ERROR: no IPL, setting IPL 2.\n"); + level = 2; + } + + if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) == 0) { + + DPRINTF(PDB_INTR, ("\npsycho: intr %lx: %p\nHunting for IRQ...\n", + (long)ino, intrlev[ino])); + + /* Hunt thru obio first */ + for (intrmapptr = &sc->sc_regs->scsi_int_map, + intrclrptr = &sc->sc_regs->scsi_clr_int; + intrmapptr <= &sc->sc_regs->ffb1_int_map; + intrmapptr++, intrclrptr++) { + if (INTINO(*intrmapptr) == ino) + goto found; + } + + /* Now do PCI interrupts */ + for (intrmapptr = &sc->sc_regs->pcia_slot0_int, + intrclrptr = &sc->sc_regs->pcia0_clr_int[0]; + intrmapptr <= &sc->sc_regs->pcib_slot3_int; + intrmapptr++, intrclrptr += 4) { + if (((*intrmapptr ^ vec) & 0x3c) == 0) { + intrclrptr += vec & 0x3; + goto found; + } + } + printf("Cannot find interrupt vector %lx\n", vec); + return (NULL); + + found: + /* Register the map and clear intr registers */ + ih->ih_map = intrmapptr; + ih->ih_clr = intrclrptr; + } +#ifdef NOT_DEBUG + if (psycho_debug & PDB_INTR) { + long i; + + for (i = 0; i < 500000000; i++) + continue; + } +#endif + + ih->ih_fun = handler; + ih->ih_arg = arg; + ih->ih_pil = level; + ih->ih_number = ino | sc->sc_ign; + + DPRINTF(PDB_INTR, ( + "; installing handler %p arg %p with ino %u pil %u\n", + handler, arg, (u_int)ino, (u_int)ih->ih_pil)); + + intr_establish(ih->ih_pil, ih); + + /* + * 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. + * + * XXXX --- we really should use bus_space for this. + */ + if (intrmapptr) { + intrmap = *intrmapptr; + DPRINTF(PDB_INTR, ("; read intrmap = %016qx", + (unsigned long long)intrmap)); + + /* Enable the interrupt */ + intrmap |= INTMAP_V; + DPRINTF(PDB_INTR, ("; addr of intrmapptr = %p", intrmapptr)); + DPRINTF(PDB_INTR, ("; writing intrmap = %016qx\n", + (unsigned long long)intrmap)); + *intrmapptr = intrmap; + DPRINTF(PDB_INTR, ("; reread intrmap = %016qx", + (unsigned long long)(intrmap = *intrmapptr))); + } + return (ih); +} + +/* + * hooks into the iommu dvma calls. + */ +int +psycho_dmamap_load(t, map, buf, buflen, p, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + void *buf; + bus_size_t buflen; + struct proc *p; + int flags; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + return (iommu_dvmamap_load(t, sc->sc_is, map, buf, buflen, p, flags)); +} + +void +psycho_dmamap_unload(t, map) + bus_dma_tag_t t; + bus_dmamap_t map; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + iommu_dvmamap_unload(t, sc->sc_is, map); +} + +int +psycho_dmamap_load_raw(t, map, segs, nsegs, size, flags) + bus_dma_tag_t t; + bus_dmamap_t map; + bus_dma_segment_t *segs; + int nsegs; + bus_size_t size; + int flags; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + return (iommu_dvmamap_load_raw(t, sc->sc_is, map, segs, nsegs, flags, size)); +} + +void +psycho_dmamap_sync(t, map, offset, len, ops) + bus_dma_tag_t t; + bus_dmamap_t map; + bus_addr_t offset; + bus_size_t len; + int ops; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + if (ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) { + /* Flush the CPU then the IOMMU */ + bus_dmamap_sync(t->_parent, map, offset, len, ops); + iommu_dvmamap_sync(t, sc->sc_is, map, offset, len, ops); + } + if (ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) { + /* Flush the IOMMU then the CPU */ + iommu_dvmamap_sync(t, sc->sc_is, map, offset, len, ops); + bus_dmamap_sync(t->_parent, map, offset, len, ops); + } + +} + +int +psycho_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags) + bus_dma_tag_t t; + bus_size_t size; + bus_size_t alignment; + bus_size_t boundary; + bus_dma_segment_t *segs; + int nsegs; + int *rsegs; + int flags; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + return (iommu_dvmamem_alloc(t, sc->sc_is, size, alignment, boundary, + segs, nsegs, rsegs, flags)); +} + +void +psycho_dmamem_free(t, segs, nsegs) + bus_dma_tag_t t; + bus_dma_segment_t *segs; + int nsegs; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + iommu_dvmamem_free(t, sc->sc_is, segs, nsegs); +} + +int +psycho_dmamem_map(t, segs, nsegs, size, kvap, flags) + bus_dma_tag_t t; + bus_dma_segment_t *segs; + int nsegs; + size_t size; + caddr_t *kvap; + int flags; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + return (iommu_dvmamem_map(t, sc->sc_is, segs, nsegs, size, kvap, flags)); +} + +void +psycho_dmamem_unmap(t, kva, size) + bus_dma_tag_t t; + caddr_t kva; + size_t size; +{ + struct psycho_pbm *pp = (struct psycho_pbm *)t->_cookie; + struct psycho_softc *sc = pp->pp_sc; + + iommu_dvmamem_unmap(t, sc->sc_is, kva, size); +} diff --git a/sys/arch/sparc64/dev/psychoreg.h b/sys/arch/sparc64/dev/psychoreg.h new file mode 100644 index 00000000000..f34595e15db --- /dev/null +++ b/sys/arch/sparc64/dev/psychoreg.h @@ -0,0 +1,354 @@ +/* $NetBSD: psychoreg.h,v 1.7 2001/07/20 00:07:13 eeh Exp $ */ + +/* + * 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. + */ +#ifndef _SPARC64_DEV_PSYCHOREG_H_ +#define _SPARC64_DEV_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 { + u_int64_t upa_portid; /* UPA port ID register */ /* 1fe.0000.0000 */ + u_int64_t upa_config; /* UPA config register */ /* 1fe.0000.0008 */ + } sys_upa; + + u_int64_t psy_csr; /* PSYCHO control/status register */ /* 1fe.0000.0010 */ + /* + * 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; + u_int64_t psy_ecccr; /* ECC control register */ /* 1fe.0000.0020 */ + u_int64_t reserved; /* 1fe.0000.0028 */ + u_int64_t psy_ue_afsr; /* Uncorrectable Error AFSR */ /* 1fe.0000.0030 */ + u_int64_t psy_ue_afar; /* Uncorrectable Error AFAR */ /* 1fe.0000.0038 */ + u_int64_t psy_ce_afsr; /* Correctable Error AFSR */ /* 1fe.0000.0040 */ + u_int64_t psy_ce_afar; /* Correctable Error AFAR */ /* 1fe.0000.0048 */ + + u_int64_t pad1[22]; + + struct perfmon { + u_int64_t pm_cr; /* Performance monitor control reg */ /* 1fe.0000.0100 */ + u_int64_t pm_count; /* Performance monitor counter reg */ /* 1fe.0000.0108 */ + } psy_pm; + + u_int64_t pad2[30]; + + struct iommureg psy_iommu; /* 1fe.0000.0200,0210 */ + + u_int64_t pad3[317]; + + u_int64_t pcia_slot0_int; /* PCI bus a slot 0 irq map reg */ /* 1fe.0000.0c00 */ + u_int64_t pcia_slot1_int; /* PCI bus a slot 1 irq map reg */ /* 1fe.0000.0c08 */ + u_int64_t pcia_slot2_int; /* PCI bus a slot 2 irq map reg (IIi)*/ /* 1fe.0000.0c10 */ + u_int64_t pcia_slot3_int; /* PCI bus a slot 3 irq map reg (IIi)*/ /* 1fe.0000.0c18 */ + u_int64_t pcib_slot0_int; /* PCI bus b slot 0 irq map reg */ /* 1fe.0000.0c20 */ + u_int64_t pcib_slot1_int; /* PCI bus b slot 1 irq map reg */ /* 1fe.0000.0c28 */ + u_int64_t pcib_slot2_int; /* PCI bus b slot 1 irq map reg */ /* 1fe.0000.0c30 */ + u_int64_t pcib_slot3_int; /* PCI bus b slot 1 irq map reg */ /* 1fe.0000.0c38 */ + + u_int64_t pad5[120]; + + u_int64_t scsi_int_map; /* SCSI interrupt map reg */ /* 1fe.0000.1000 */ + u_int64_t ether_int_map; /* ethernet interrupt map reg */ /* 1fe.0000.1008 */ + u_int64_t bpp_int_map; /* parallel interrupt map reg */ /* 1fe.0000.1010 */ + u_int64_t audior_int_map; /* audio record interrupt map reg */ /* 1fe.0000.1018 */ + u_int64_t audiop_int_map; /* audio playback interrupt map reg */ /* 1fe.0000.1020 */ + u_int64_t power_int_map; /* power fail interrupt map reg */ /* 1fe.0000.1028 */ + u_int64_t ser_kbd_ms_int_map; /* serial/kbd/mouse interrupt map reg *//* 1fe.0000.1030 */ + u_int64_t fd_int_map; /* floppy interrupt map reg */ /* 1fe.0000.1038 */ + u_int64_t spare_int_map; /* spare interrupt map reg */ /* 1fe.0000.1040 */ + u_int64_t kbd_int_map; /* kbd [unused] interrupt map reg */ /* 1fe.0000.1048 */ + u_int64_t mouse_int_map; /* mouse [unused] interrupt map reg */ /* 1fe.0000.1050 */ + u_int64_t serial_int_map; /* second serial interrupt map reg */ /* 1fe.0000.1058 */ + u_int64_t timer0_int_map; /* timer 0 interrupt map reg */ /* 1fe.0000.1060 */ + u_int64_t timer1_int_map; /* timer 1 interrupt map reg */ /* 1fe.0000.1068 */ + u_int64_t ue_int_map; /* UE interrupt map reg */ /* 1fe.0000.1070 */ + u_int64_t ce_int_map; /* CE interrupt map reg */ /* 1fe.0000.1078 */ + u_int64_t pciaerr_int_map; /* PCI bus a error interrupt map reg */ /* 1fe.0000.1080 */ + u_int64_t pciberr_int_map; /* PCI bus b error interrupt map reg */ /* 1fe.0000.1088 */ + u_int64_t pwrmgt_int_map; /* power mgmt wake interrupt map reg */ /* 1fe.0000.1090 */ + u_int64_t ffb0_int_map; /* FFB0 graphics interrupt map reg */ /* 1fe.0000.1098 */ + u_int64_t ffb1_int_map; /* FFB1 graphics interrupt map reg */ /* 1fe.0000.10a0 */ + + u_int64_t pad6[107]; + + /* Note: clear interrupt 0 registers are not really used */ + u_int64_t pcia0_clr_int[4]; /* PCI a slot 0 clear int regs 0..7 */ /* 1fe.0000.1400-1418 */ + u_int64_t pcia1_clr_int[4]; /* PCI a slot 1 clear int regs 0..7 */ /* 1fe.0000.1420-1438 */ + u_int64_t pcia2_clr_int[4]; /* PCI a slot 2 clear int regs 0..7 */ /* 1fe.0000.1440-1458 */ + u_int64_t pcia3_clr_int[4]; /* PCI a slot 3 clear int regs 0..7 */ /* 1fe.0000.1480-1478 */ + u_int64_t pcib0_clr_int[4]; /* PCI b slot 0 clear int regs 0..7 */ /* 1fe.0000.1480-1498 */ + u_int64_t pcib1_clr_int[4]; /* PCI b slot 1 clear int regs 0..7 */ /* 1fe.0000.14a0-14b8 */ + u_int64_t pcib2_clr_int[4]; /* PCI b slot 2 clear int regs 0..7 */ /* 1fe.0000.14c0-14d8 */ + u_int64_t pcib3_clr_int[4]; /* PCI b slot 3 clear int regs 0..7 */ /* 1fe.0000.14d0-14f8 */ + + u_int64_t pad8[96]; + + u_int64_t scsi_clr_int; /* SCSI clear int reg */ /* 1fe.0000.1800 */ + u_int64_t ether_clr_int; /* ethernet clear int reg */ /* 1fe.0000.1808 */ + u_int64_t bpp_clr_int; /* parallel clear int reg */ /* 1fe.0000.1810 */ + u_int64_t audior_clr_int; /* audio record clear int reg */ /* 1fe.0000.1818 */ + u_int64_t audiop_clr_int; /* audio playback clear int reg */ /* 1fe.0000.1820 */ + u_int64_t power_clr_int; /* power fail clear int reg */ /* 1fe.0000.1828 */ + u_int64_t ser_kb_ms_clr_int; /* serial/kbd/mouse clear int reg */ /* 1fe.0000.1830 */ + u_int64_t fd_clr_int; /* floppy clear int reg */ /* 1fe.0000.1838 */ + u_int64_t spare_clr_int; /* spare clear int reg */ /* 1fe.0000.1840 */ + u_int64_t kbd_clr_int; /* kbd [unused] clear int reg */ /* 1fe.0000.1848 */ + u_int64_t mouse_clr_int; /* mouse [unused] clear int reg */ /* 1fe.0000.1850 */ + u_int64_t serial_clr_int; /* second serial clear int reg */ /* 1fe.0000.1858 */ + u_int64_t timer0_clr_int; /* timer 0 clear int reg */ /* 1fe.0000.1860 */ + u_int64_t timer1_clr_int; /* timer 1 clear int reg */ /* 1fe.0000.1868 */ + u_int64_t ue_clr_int; /* UE clear int reg */ /* 1fe.0000.1870 */ + u_int64_t ce_clr_int; /* CE clear int reg */ /* 1fe.0000.1878 */ + u_int64_t pciaerr_clr_int; /* PCI bus a error clear int reg */ /* 1fe.0000.1880 */ + u_int64_t pciberr_clr_int; /* PCI bus b error clear int reg */ /* 1fe.0000.1888 */ + u_int64_t pwrmgt_clr_int; /* power mgmt wake clr interrupt reg */ /* 1fe.0000.1890 */ + + u_int64_t pad9[45]; + + u_int64_t intr_retry_timer; /* interrupt retry timer */ /* 1fe.0000.1a00 */ + + u_int64_t pad10[63]; + + struct timer_counter { + u_int64_t tc_count; /* timer/counter 0/1 count register */ /* 1fe.0000.1c00,1c10 */ + u_int64_t tc_limit; /* timer/counter 0/1 limit register */ /* 1fe.0000.1c08,1c18 */ + } tc[2]; + + u_int64_t pci_dma_write_sync; /* PCI DMA write sync register (IIi) */ /* 1fe.0000.1c20 */ + + u_int64_t pad11[123]; + + struct pci_ctl { + u_int64_t pci_csr; /* PCI a/b control/status register */ /* 1fe.0000.2000,4000 */ + u_int64_t pci_afsr; /* PCI a/b AFSR register */ /* 1fe.0000.2010,4010 */ + u_int64_t pci_afar; /* PCI a/b AFAR register */ /* 1fe.0000.2018,4018 */ + u_int64_t pci_diag; /* PCI a/b diagnostic register */ /* 1fe.0000.2020,4020 */ + u_int64_t pci_tasr; /* PCI target address space reg (IIi)*/ /* 1fe.0000.2028,4028 */ + + u_int64_t pad12[251]; + + /* This is really the IOMMU's, not the PCI bus's */ + struct iommu_strbuf pci_strbuf; /* 1fe.0000.2800-210 */ +#define psy_iommu_strbuf psy_pcictl[0].pci_strbuf + + u_int64_t pad13[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 pad14[2048]; + + u_int64_t dma_scb_diag0; /* DMA scoreboard diag reg 0 */ /* 1fe.0000.a000 */ + u_int64_t dma_scb_diag1; /* DMA scoreboard diag reg 1 */ /* 1fe.0000.a008 */ + + u_int64_t pad15[126]; + + u_int64_t iommu_svadiag; /* IOMMU virtual addr diag reg */ /* 1fe.0000.a400 */ + u_int64_t iommu_tlb_comp_diag; /* IOMMU TLB tag compare diag reg */ /* 1fe.0000.a408 */ + + u_int64_t pad16[30]; + + u_int64_t iommu_queue_diag[16]; /* IOMMU LRU queue diag */ /* 1fe.0000.a500-a578 */ + u_int64_t tlb_tag_diag[16]; /* TLB tag diag */ /* 1fe.0000.a580-a5f8 */ + u_int64_t tlb_data_diag[16]; /* TLB data RAM diag */ /* 1fe.0000.a600-a678 */ + + u_int64_t pad17[48]; + + u_int64_t pci_int_diag; /* SBUS int state diag reg */ /* 1fe.0000.a800 */ + u_int64_t obio_int_diag; /* OBIO and misc int state diag reg */ /* 1fe.0000.a808 */ + + u_int64_t pad18[254]; + + struct strbuf_diag { + u_int64_t strbuf_data_diag[128]; /* streaming buffer data RAM diag */ /* 1fe.0000.b000-b3f8 */ + u_int64_t strbuf_error_diag[128]; /* streaming buffer error status diag *//* 1fe.0000.b400-b7f8 */ + u_int64_t strbuf_pg_tag_diag[16]; /* streaming buffer page tag diag */ /* 1fe.0000.b800-b878 */ + u_int64_t pad19[16]; + u_int64_t strbuf_ln_tag_diag[16]; /* streaming buffer line tag diag */ /* 1fe.0000.b900-b978 */ + u_int64_t pad20[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) + */ +}; + +/* 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; + +struct psycho_registers { + u_int32_t phys_hi; + u_int32_t phys_mid; + u_int32_t phys_lo; + u_int32_t size_hi; + u_int32_t size_lo; +}; + +struct psycho_ranges { + u_int32_t cspace; + u_int32_t child_hi; + u_int32_t child_lo; + u_int32_t phys_hi; + u_int32_t phys_lo; + u_int32_t size_hi; + u_int32_t size_lo; +}; + +struct psycho_interrupt_map { + 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 psycho_interrupt_map_mask { + u_int32_t phys_hi; + u_int32_t phys_mid; + u_int32_t phys_lo; + u_int32_t intr; +}; + +#endif /* _SPARC64_DEV_PSYCHOREG_H_ */ diff --git a/sys/arch/sparc64/dev/psychovar.h b/sys/arch/sparc64/dev/psychovar.h new file mode 100644 index 00000000000..de28ee21bd2 --- /dev/null +++ b/sys/arch/sparc64/dev/psychovar.h @@ -0,0 +1,119 @@ +/* $NetBSD: psychovar.h,v 1.6 2001/07/20 00:07:13 eeh Exp $ */ + +/* + * 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. + */ + +#ifndef _SPARC64_DEV_PSYCHOVAR_H_ +#define _SPARC64_DEV_PSYCHOVAR_H_ + +/* per real PCI bus info */ +struct psycho_softc; + +struct psycho_pbm { + /* link to mum */ + struct psycho_softc *pp_sc; + + /* + * 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 psycho_registers *pp_regs; + struct psycho_ranges *pp_range; + + /* counts of above */ + int pp_nregs; + int pp_nrange; + int pp_nintmap; + + /* chipset tag for this instance */ + pci_chipset_tag_t pp_pc; + + /* our tags */ + bus_space_tag_t pp_memt; + bus_space_tag_t pp_iot; + bus_dma_tag_t pp_dmat; + int pp_bus; + int pp_flags; + + /* and pointers into the psycho regs for our bits */ + struct pci_ctl *pp_pcictl; +}; + +/* + * per-PCI bus on mainbus softc structure; one for sabre, or two + * per pair of psycho's. + */ +struct psycho_softc { + struct device sc_dev; + + /* + * one sabre has two simba's. psycho's are separately attached, + * with the `other' psycho_pbm allocated at the first's attach. + */ + struct psycho_pbm *__sc_psycho_this; + struct psycho_pbm *__sc_psycho_other; +#define sc_psycho_this __sc_psycho_this +#define sc_psycho_other __sc_psycho_other + + /* + * PSYCHO register. we record the base physical address of these + * also as it is the base of the entire PSYCHO + */ + struct psychoreg *sc_regs; + paddr_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; + + /* config space */ + bus_space_tag_t sc_configtag; + bus_space_handle_t sc_configaddr; + + int sc_clockfreq; + int sc_node; /* prom node */ + int sc_mode; /* (whatareya?) */ +#define PSYCHO_MODE_SABRE 1 /* i'm a sabre (yob) */ +#define PSYCHO_MODE_PSYCHO 2 /* i'm a psycho (w*nker) */ + + struct iommu_state *sc_is; +}; + +/* config space is per-psycho. mem/io/dma are per-pci bus */ +bus_dma_tag_t psycho_alloc_dma_tag __P((struct psycho_pbm *)); +bus_space_tag_t psycho_alloc_bus_tag __P((struct psycho_pbm *, int)); + +#define psycho_alloc_config_tag(pp) psycho_alloc_bus_tag((pp), PCI_CONFIG_BUS_SPACE) +#define psycho_alloc_mem_tag(pp) psycho_alloc_bus_tag((pp), PCI_MEMORY_BUS_SPACE) +#define psycho_alloc_io_tag(pp) psycho_alloc_bus_tag((pp), PCI_IO_BUS_SPACE) + +#endif /* _SPARC64_DEV_PSYCHOVAR_H_ */ |