diff options
-rw-r--r-- | sys/alpha/tlsb/dwlpx.c | 835 |
1 files changed, 740 insertions, 95 deletions
diff --git a/sys/alpha/tlsb/dwlpx.c b/sys/alpha/tlsb/dwlpx.c index fbf21ec..f3a8e3b 100644 --- a/sys/alpha/tlsb/dwlpx.c +++ b/sys/alpha/tlsb/dwlpx.c @@ -26,6 +26,38 @@ * $FreeBSD$ */ +/* + * Based very closely on NetBSD version- + * + * Copyright (c) 1997 by Matthew Jacob + * NASA AMES Research Center. + * 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 immediately at the beginning of the file, without modification, + * 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. + */ + #include "opt_simos.h" #include <sys/param.h> @@ -33,31 +65,97 @@ #include <sys/kernel.h> #include <sys/module.h> #include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> #include <machine/swiz.h> -#include <alpha/tlsb/dwlpxreg.h> +#include <machine/intr.h> +#include <machine/intrcnt.h> +#include <machine/resource.h> +#include <machine/sgmap.h> +#include <vm/vm.h> +#include <vm/vm_page.h> + #include <alpha/tlsb/tlsbreg.h> #include <alpha/tlsb/tlsbvar.h> +#include <alpha/tlsb/kftxxreg.h> #include <alpha/tlsb/kftxxvar.h> -#define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) -#define DWLPX_BASE(n, h) ((((u_long)(n) - 4) << 36) \ - | ((u_long)(h) << 34) \ - | (1L << 39)) +#include <alpha/tlsb/dwlpxreg.h> +#include <alpha/tlsb/dwlpxvar.h> +#include <alpha/pci/pcibus.h> +#include <pci/pcivar.h> static devclass_t dwlpx_devclass; -static device_t dwlpx0; /* XXX only one for now */ +static device_t dwlpxs[DWLPX_NIONODE][DWLPX_NHOSE]; + + +#define KV(pa) ((void *)ALPHA_PHYS_TO_K0SEG(pa)) struct dwlpx_softc { + struct dwlpx_softc *next; + device_t dev; /* backpointer */ + u_int64_t sysbase; /* shorthand */ vm_offset_t dmem_base; /* dense memory */ vm_offset_t smem_base; /* sparse memory */ vm_offset_t io_base; /* sparse i/o */ - vm_offset_t cfg_base; /* sparse pci config */ + int bushose; /* our bus && hose */ + u_int : 26, + nhpc : 2, /* how many HPCs */ + dwlpb : 1, /* this is a DWLPB */ + sgmapsz : 3; /* Scatter Gather map size */ }; -#define DWLPX_SOFTC(dev) (struct dwlpx_softc*) device_get_softc(dev) +static int dwlpx_probe(device_t dev); +static int dwlpx_attach(device_t dev); + +static int dwlpx_setup_intr(device_t, device_t, struct resource *, int, + driver_intr_t *, void *, void **); +static int +dwlpx_teardown_intr(device_t, device_t, struct resource *, void *); +static driver_intr_t dwlpx_intr; + +static device_method_t dwlpx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dwlpx_probe), + DEVMETHOD(device_attach, dwlpx_attach), + + /* Bus interface */ + DEVMETHOD(bus_setup_intr, dwlpx_setup_intr), + DEVMETHOD(bus_teardown_intr, dwlpx_teardown_intr), + DEVMETHOD(bus_alloc_resource, pci_alloc_resource), + DEVMETHOD(bus_release_resource, pci_release_resource), + DEVMETHOD(bus_activate_resource, pci_activate_resource), + DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), + + { 0, 0 } +}; +static driver_t dwlpx_driver = { + "dwlpx", dwlpx_methods, sizeof (struct dwlpx_softc) +}; + +static u_int32_t imaskcache[DWLPX_NIONODE][DWLPX_NHOSE][NHPC]; +static void dwlpx_eintr(unsigned long); + +/* + * Direct-mapped window: 2G at 2G + */ +#define DWLPx_DIRECT_MAPPED_BASE (2UL*1024UL*1024UL*1024UL) +#define DWLPx_DIRECT_MAPPED_SIZE (2UL*1024UL*1024UL*1024UL) +#define DWLPx_DIRECT_MAPPED_WMASK PCIA_WMASK_2G + +/* + * SGMAP window A: 256M at 1.75G or 1G at 1G + */ +#define DWLPx_SG_MAPPED_SIZE(x) ((x) * PAGE_SIZE) +static void dwlpx_dma_init(struct dwlpx_softc *); + + + +#define DWLPX_SOFTC(dev) (struct dwlpx_softc *) device_get_softc(dev) +static struct dwlpx_softc *dwlpx_root; static alpha_chipset_inb_t dwlpx_inb; static alpha_chipset_inw_t dwlpx_inw; @@ -101,220 +199,767 @@ static alpha_chipset_t dwlpx_chipset = { dwlpx_cfgwritel, }; -/* - * For supporting multiple busses, we will encode the dwlpx unit number into - * the port address as Linux does. - */ +#define DWLPX_IONODE(port) ((port >> 29) & 0x7) +#define DWLPX_HOSE(port) ((port >> 27) & 0x3) +#define DWLPX_INST(port) dwlpxs[DWLPX_IONODE(port)][DWLPX_HOSE(port)] +#define DWLPX_ADDR(port) (port & 0x07ffffff) static u_int8_t dwlpx_inb(u_int32_t port) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - return SPARSE_READ_BYTE(sc->io_base, port); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(port)); + return SPARSE_READ_BYTE(sc->io_base, DWLPX_ADDR(port)); } static u_int16_t dwlpx_inw(u_int32_t port) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - return SPARSE_READ_WORD(sc->io_base, port); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(port)); + return SPARSE_READ_WORD(sc->io_base, DWLPX_ADDR(port)); } static u_int32_t dwlpx_inl(u_int32_t port) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - return SPARSE_READ_LONG(sc->io_base, port); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(port)); + return SPARSE_READ_LONG(sc->io_base, DWLPX_ADDR(port)); } static void dwlpx_outb(u_int32_t port, u_int8_t data) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - - SPARSE_WRITE_BYTE(sc->io_base, port, data); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(port)); + SPARSE_WRITE_BYTE(sc->io_base, DWLPX_ADDR(port), data); + alpha_mb(); } static void dwlpx_outw(u_int32_t port, u_int16_t data) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - SPARSE_WRITE_WORD(sc->io_base, port, data); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(port)); + SPARSE_WRITE_WORD(sc->io_base, DWLPX_ADDR(port), data); + alpha_mb(); } static void dwlpx_outl(u_int32_t port, u_int32_t data) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - SPARSE_WRITE_LONG(sc->io_base, port, data); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(port)); + SPARSE_WRITE_LONG(sc->io_base, DWLPX_ADDR(port), data); + alpha_mb(); } static u_int8_t dwlpx_readb(u_int32_t pa) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - return SPARSE_READ_BYTE(sc->smem_base, pa); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(pa)); + return SPARSE_READ_BYTE(sc->smem_base, DWLPX_ADDR(pa)); } static u_int16_t dwlpx_readw(u_int32_t pa) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - return SPARSE_READ_WORD(sc->smem_base, pa); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(pa)); + return SPARSE_READ_WORD(sc->smem_base, DWLPX_ADDR(pa)); } static u_int32_t dwlpx_readl(u_int32_t pa) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - return SPARSE_READ_LONG(sc->smem_base, pa); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(pa)); + return SPARSE_READ_LONG(sc->smem_base, DWLPX_ADDR(pa)); } static void dwlpx_writeb(u_int32_t pa, u_int8_t data) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - - SPARSE_WRITE_BYTE(sc->smem_base, pa, data); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(pa)); + SPARSE_WRITE_BYTE(sc->smem_base, DWLPX_ADDR(pa), data); + alpha_mb(); } static void dwlpx_writew(u_int32_t pa, u_int16_t data) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - SPARSE_WRITE_WORD(sc->smem_base, pa, data); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(pa)); + SPARSE_WRITE_WORD(sc->smem_base, DWLPX_ADDR(pa), data); + alpha_mb(); } static void dwlpx_writel(u_int32_t pa, u_int32_t data) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); - SPARSE_WRITE_LONG(sc->smem_base, pa, data); + struct dwlpx_softc *sc = DWLPX_SOFTC(DWLPX_INST(pa)); + SPARSE_WRITE_LONG(sc->smem_base, DWLPX_ADDR(pa), data); + alpha_mb(); } static int dwlpx_maxdevs(u_int b) { - return 12; /* XXX */ + return (DWLPX_MAXDEV); } -/* XXX only support bus 0 */ +static u_int32_t dwlpx_cfgread(u_int, u_int, u_int, u_int, u_int, int); +static void dwlpx_cfgwrite(u_int, u_int, u_int, u_int, u_int, int, u_int32_t); + +static u_int32_t +dwlpx_cfgread(u_int bh, u_int bus, u_int slot, u_int func, u_int off, int sz) +{ + struct dwlpx_softc *sc; + device_t dev; + u_int32_t *dp, data, rvp, pci_idsel, hpcdev; + unsigned long paddr; + int hose, ionode; + int secondary = 0, s = 0, i; + + rvp = data = ~0; + + ionode = ((bh >> 2) & 0x7); + hose = (bh & 0x3); + dev = dwlpxs[ionode][hose]; + if (dev == (device_t) 0) { + return (data); + } + sc = DWLPX_SOFTC(dev); + if (sc->nhpc < 1) + return (data); + else if (sc->nhpc < 2 && slot >= 4) + return (data); + else if (sc->nhpc < 3 && slot >= 8) + return (data); + else if (slot >= DWLPX_MAXDEV) + return (data); + hpcdev = slot >> 2; + pci_idsel = (1 << ((slot & 0x3) + 2)); + paddr = (hpcdev << 22) | (pci_idsel << 16) | (func << 13); + + if (secondary) { + paddr &= 0x1fffff; + paddr |= (secondary << 21); + +#if 0 + printf("read secondary %d reg %x (paddr %lx)", + secondary, offset, tag); +#endif + + alpha_pal_draina(); + s = splhigh(); + /* + * Set up HPCs for type 1 cycles. + */ + for (i = 0; i < sc->nhpc; i++) { + rvp = REGVAL(PCIA_CTL(i)+sc->sysbase) | PCIA_CTL_T1CYC; + alpha_mb(); + REGVAL(PCIA_CTL(i) + sc->sysbase) = rvp; + alpha_mb(); + } + } + + paddr |= ((unsigned long) ((off >> 2) << 7)); + paddr |= ((sz - 1) << 3); + paddr |= DWLPX_PCI_CONF; + paddr |= ((unsigned long) hose) << 34; + paddr |= ((unsigned long) ionode) << 36; + paddr |= 1L << 39; + + dp = (u_int32_t *)KV(paddr); + +#if 0 +printf("CFGREAD %d.%d.%d.%d.%d.%d.%d -> paddr 0x%lx", +ionode+4, hose, bus, slot, func, off, sz, paddr); +#endif -#define DWLPX_CFGOFF(b, s, f, r) \ - (((b) << 16) | ((s) << 11) | ((f) << 8) | (r)) + if (badaddr(dp, sizeof (*dp)) == 0) { + data = *dp; + } + + if (secondary) { + alpha_pal_draina(); + for (i = 0; i < sc->nhpc; i++) { + rvp = REGVAL(PCIA_CTL(i)+sc->sysbase) & ~PCIA_CTL_T1CYC; + alpha_mb(); + REGVAL(PCIA_CTL(i) + sc->sysbase) = rvp; + alpha_mb(); + } + (void) splx(s); + } + + if (data != ~0) { + if (sz == 1) { + rvp = SPARSE_BYTE_EXTRACT(off, data); + } else if (sz == 2) { + rvp = SPARSE_WORD_EXTRACT(off, data); + } else { + rvp = data; + } + } else { + rvp = data; + } + +#if 0 +printf(" data 0x%x -> 0x%x\n", data, rvp); +#endif + return (rvp); +} -#define CFGREAD(b, s, f, r, width) \ - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); \ - vm_offset_t off = DWLPX_CFGOFF(b, s, f, r); \ - vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg_base, off); \ - if (badaddr((caddr_t)kv, 4)) return ~0; \ - return SPARSE_##width##_EXTRACT(off, SPARSE_READ(kv)) +static void +dwlpx_cfgwrite(u_int bh, u_int bus, u_int slot, u_int func, u_int off, + int sz, u_int32_t data) +{ + int hose, ionode; + int secondary = 0, s = 0, i; + u_int32_t *dp, rvp, pci_idsel, hpcdev; + unsigned long paddr; + struct dwlpx_softc *sc; + device_t dev; + + ionode = ((bh >> 2) & 0x7); + hose = (bh & 0x3); + dev = dwlpxs[ionode][hose]; + if (dev == (device_t) 0) { + return; + } + sc = DWLPX_SOFTC(dev); + if (sc->nhpc < 1) + return; + else if (sc->nhpc < 2 && slot >= 4) + return; + else if (sc->nhpc < 3 && slot >= 8) + return; + else if (slot >= DWLPX_MAXDEV) + return; + hpcdev = slot >> 2; + pci_idsel = (1 << ((slot & 0x3) + 2)); + paddr = (hpcdev << 22) | (pci_idsel << 16) | (func << 13); + + if (secondary) { + paddr &= 0x1fffff; + paddr |= (secondary << 21); + +#if 0 + printf("write secondary %d reg %x (paddr %lx)", + secondary, offset, tag); +#endif -#define CFGWRITE(b, s, f, r, data, width) \ - struct dwlpx_softc* sc = DWLPX_SOFTC(dwlpx0); \ - vm_offset_t off = DWLPX_CFGOFF(b, s, f, r); \ - vm_offset_t kv = SPARSE_##width##_ADDRESS(sc->cfg_base, off); \ - if (badaddr((caddr_t)kv, 4)) return; \ - SPARSE_WRITE(kv, SPARSE_##width##_INSERT(off, data)) + alpha_pal_draina(); + s = splhigh(); + /* + * Set up HPCs for type 1 cycles. + */ + for (i = 0; i < sc->nhpc; i++) { + rvp = REGVAL(PCIA_CTL(i)+sc->sysbase) | PCIA_CTL_T1CYC; + alpha_mb(); + REGVAL(PCIA_CTL(i) + sc->sysbase) = rvp; + alpha_mb(); + } + } + + paddr |= ((unsigned long) ((off >> 2) << 7)); + paddr |= ((sz - 1) << 3); + paddr |= DWLPX_PCI_CONF; + paddr |= ((unsigned long) hose) << 34; + paddr |= ((unsigned long) ionode) << 36; + paddr |= 1L << 39; + + dp = (u_int32_t *)KV(paddr); + if (badaddr(dp, sizeof (*dp)) == 0) { + u_int32_t new_data; + if (sz == 1) { + new_data = SPARSE_BYTE_INSERT(off, data); + } else if (sz == 2) { + new_data = SPARSE_WORD_INSERT(off, data); + } else { + new_data = data; + } + +#if 0 +printf("CFGWRITE %d.%d.%d.%d.%d.%d.%d paddr 0x%lx data 0x%x -> 0x%x\n", +ionode+4, hose, bus, slot, func, off, sz, paddr, data, new_data); +#endif + + *dp = new_data; + } + if (secondary) { + alpha_pal_draina(); + for (i = 0; i < sc->nhpc; i++) { + rvp = REGVAL(PCIA_CTL(i)+sc->sysbase) & ~PCIA_CTL_T1CYC; + alpha_mb(); + REGVAL(PCIA_CTL(i) + sc->sysbase) = rvp; + alpha_mb(); + } + (void) splx(s); + } +} static u_int8_t dwlpx_cfgreadb(u_int h, u_int b, u_int s, u_int f, u_int r) { - CFGREAD(b, s, f, r, BYTE); + return (u_int8_t) dwlpx_cfgread(h, b, s, f, r, 1); } static u_int16_t dwlpx_cfgreadw(u_int h, u_int b, u_int s, u_int f, u_int r) { - CFGREAD(b, s, f, r, WORD); + return (u_int16_t) dwlpx_cfgread(h, b, s, f, r, 2); } static u_int32_t dwlpx_cfgreadl(u_int h, u_int b, u_int s, u_int f, u_int r) { - CFGREAD(b, s, f, r, LONG); + return dwlpx_cfgread(h, b, s, f, r, 4); } static void dwlpx_cfgwriteb(u_int h, u_int b, u_int s, u_int f, u_int r, u_int8_t data) { - CFGWRITE(b, s, f, r, data, BYTE); + dwlpx_cfgwrite(h, b, s, f, r, 1, (u_int32_t) data); } static void dwlpx_cfgwritew(u_int h, u_int b, u_int s, u_int f, u_int r, u_int16_t data) { - CFGWRITE(b, s, f, r, data, WORD); + dwlpx_cfgwrite(h, b, s, f, r, 2, (u_int32_t) data); } static void dwlpx_cfgwritel(u_int h, u_int b, u_int s, u_int f, u_int r, u_int32_t data) { - CFGWRITE(b, s, f, r, data, LONG); + dwlpx_cfgwrite(h, b, s, f, r, 4, (u_int32_t) data); } -static int dwlpx_probe(device_t dev); -static int dwlpx_attach(device_t dev); -static driver_intr_t dwlpx_intr; - -static device_method_t dwlpx_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, dwlpx_probe), - DEVMETHOD(device_attach, dwlpx_attach), - - { 0, 0 } -}; - -static driver_t dwlpx_driver = { - "dwlpx", - dwlpx_methods, - sizeof(struct dwlpx_softc), -}; - static int dwlpx_probe(device_t dev) { - if (dwlpx0) - return ENXIO; - dwlpx0 = dev; - device_set_desc(dev, "DWLPA or DWLPB PCI adapter"); - return 0; + device_t child; + u_int32_t ctl; + struct dwlpx_softc *xc, *sc = DWLPX_SOFTC(dev); + unsigned long ls; + int io, hose; + + io = kft_get_node(dev) - 4; + hose = kft_get_hosenum(dev); + + sc->bushose = (io << 2) | hose; + + if (dwlpxs[io][hose]) { + printf("%s: already attached\n", device_get_nameunit(dev)); + return EEXIST; + } + if ((xc = dwlpx_root) == NULL) { + dwlpx_root = sc; + } else { + while (xc->next) + xc = xc->next; + xc->next = sc; + } + sc->dev = dwlpxs[io][hose] = dev; + + ls = DWLPX_BASE(io + 4, hose); + for (sc->nhpc = 1; sc->nhpc < NHPC; sc->nhpc++) { + if (badaddr(KV(PCIA_CTL(sc->nhpc) + ls), sizeof (ctl))) { + break; + } + } + if (sc->nhpc != NHPC) { + REGVAL(PCIA_ERR(0) + ls) = PCIA_ERR_ALLERR; + } + ctl = REGVAL(PCIA_PRESENT + ls); + if ((ctl >> PCIA_PRESENT_REVSHIFT) & PCIA_PRESENT_REVMASK) { + sc->dwlpb = 1; + device_set_desc(dev, "DWLPB PCI adapter"); + } else { + device_set_desc(dev, "DWLPA PCI adapter"); + } + sc->sgmapsz = DWLPX_SG32K; + + if (device_get_unit(dev) == 0) { + pci_init_resources(); + } + + child = device_add_child(dev, "pcib", device_get_unit(dev)); + device_set_ivars(child, &sc->bushose); + return (0); } static int dwlpx_attach(device_t dev) { - struct dwlpx_softc* sc = DWLPX_SOFTC(dev); + struct dwlpx_softc *sc = DWLPX_SOFTC(dev); device_t parent = device_get_parent(dev); vm_offset_t regs; + u_int32_t ctl; + int i, io, hose; void *intr; - dwlpx0 = dev; + io = kft_get_node(dev) - 4; + hose = kft_get_hosenum(dev); chipset = dwlpx_chipset; /* chipset.intrdev = dev; */ - regs = KV(DWLPX_BASE(kft_get_node(dev), kft_get_hosenum(dev))); - sc->dmem_base = regs + (0L << 32); - sc->smem_base = regs + (1L << 32); - sc->io_base = regs + (2L << 32); - sc->cfg_base = regs + (3L << 32); + sc->sysbase = DWLPX_BASE(io + 4, hose); + regs = (vm_offset_t) KV(sc->sysbase); + sc->dmem_base = regs + DWLPX_PCI_DENSE; + sc->smem_base = regs + DWLPX_PCI_SPARSE; + sc->io_base = regs + DWLPX_PCI_IOSPACE; + + /* + * Set up interrupt stuff for this DWLPX. + * + * Note that all PCI interrupt pins are disabled at this time. + * + * Do this even for all HPCs- even for the + * nonexistent one on hose zero of a KFTIA. + */ + for (i = 0; i < NHPC; i++) { + REGVAL(PCIA_IMASK(i) + sc->sysbase) = DWLPX_IMASK_DFLT; + REGVAL(PCIA_ERRVEC(i) + sc->sysbase) = + DWLPX_ERRVEC(io, hose); + } + + for (i = 0; i < DWLPX_MAXDEV; i++) { + u_int16_t vec; + int ss, hpc; + + vec = DWLPX_MVEC(io, hose, i); + ss = i; + if (i < 4) { + hpc = 0; + } else if (i < 8) { + ss -= 4; + hpc = 1; + } else { + ss -= 8; + hpc = 2; + } + REGVAL(PCIA_DEVVEC(hpc, ss, 1) + sc->sysbase) = vec; + REGVAL(PCIA_DEVVEC(hpc, ss, 2) + sc->sysbase) = vec; + REGVAL(PCIA_DEVVEC(hpc, ss, 3) + sc->sysbase) = vec; + REGVAL(PCIA_DEVVEC(hpc, ss, 4) + sc->sysbase) = vec; + } + + /* + * Establish HAE values, as well as make sure of sanity elsewhere. + */ + for (i = 0; i < sc->nhpc; i++) { + ctl = REGVAL(PCIA_CTL(i) + sc->sysbase); + ctl &= 0x0fffffff; + ctl &= ~(PCIA_CTL_MHAE(0x1f) | PCIA_CTL_IHAE(0x1f)); + /* + * I originally also had it or'ing in 3, which makes no sense. + */ + + ctl |= PCIA_CTL_RMMENA | PCIA_CTL_RMMARB; + + /* + * Only valid if we're attached to a KFTIA or a KTHA. + */ + ctl |= PCIA_CTL_3UP; + + ctl |= PCIA_CTL_CUTENA; + + /* + * Fit in appropriate S/G Map Ram size. + */ + if (sc->sgmapsz == DWLPX_SG32K) + ctl |= PCIA_CTL_SG32K; + else if (sc->sgmapsz == DWLPX_SG128K) + ctl |= PCIA_CTL_SG128K; + else + ctl |= PCIA_CTL_SG32K; + + REGVAL(PCIA_CTL(i) + sc->sysbase) = ctl; + } + + /* + * Enable TBIT if required + */ + if (sc->sgmapsz == DWLPX_SG128K) + REGVAL(PCIA_TBIT + sc->sysbase) = 1; + + alpha_mb(); + + for (io = 0; io < DWLPX_NIONODE; io++) { + for (hose = 0; hose < DWLPX_NHOSE; hose++) { + for (i = 0; i < NHPC; i++) { + imaskcache[io][hose][i] = DWLPX_IMASK_DFLT; + } + } + } + + /* + * Set up DMA stuff here. + */ + + dwlpx_dma_init(sc); + + + /* + * Register our interrupt service requirements with out parent. + */ + i = BUS_SETUP_INTR(parent, dev, NULL, + INTR_TYPE_MISC, dwlpx_intr, 0, &intr); + if (i == 0) { + bus_generic_attach(dev); + } + return (i); +} - *(u_int32_t*) (regs + PCIA_CTL(0)) = 1; /* Type1 config cycles */ +static void dwlpx_enadis_intr(int, int, int); + +static void +dwlpx_enadis_intr(int vector, int intpin, int onoff) +{ + unsigned long paddr; + u_int32_t val; + int device, ionode, hose, hpc, s; + + ionode = DWLPX_MVEC_IONODE(vector); + hose = DWLPX_MVEC_HOSE(vector); + device = DWLPX_MVEC_PCISLOT(vector); + + paddr = (1LL << 39); + paddr |= (unsigned long) ionode << 36; + paddr |= (unsigned long) hose << 34; + if (device < 4) { + hpc = 0; + } else if (device < 8) { + hpc = 1; + device -= 4; + } else { + hpc = 2; + device -= 8; + } + intpin <<= (device << 2); + val = imaskcache[ionode][hose][hpc]; + if (onoff) + val |= intpin; + else + val &= ~intpin; + imaskcache[ionode][hose][hpc] = val; + s = splhigh(); + REGVAL(PCIA_IMASK(hpc) + paddr) = val; + alpha_mb(); + splx(s); +} - return BUS_SETUP_INTR(parent, dev, NULL, INTR_TYPE_MISC, dwlpx_intr, 0, &intr); +static int +dwlpx_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, + driver_intr_t *intr, void *arg, void **cookiep) +{ + int slot, ionode, hose, error, vector, intpin; + + error = rman_activate_resource(irq); + if (error) + return error; + + intpin = pci_get_intpin(child); + slot = pci_get_slot(child); + hose = pci_get_hose(child); + ionode = hose >> 2; + hose &= 0x3; + + vector = DWLPX_MVEC(ionode, hose, slot); + error = alpha_setup_intr(vector, intr, arg, cookiep, + &intrcnt[INTRCNT_KN8AE_IRQ]); + if (error) + return error; + dwlpx_enadis_intr(vector, intpin, 1); + device_printf(child, "Node %d Hose %d Slot %d interrupting at TLSB " + "vector 0x%x intpin %d\n", ionode+4, hose, slot, vector, intpin); + return (0); +} - return 0; +static int +dwlpx_teardown_intr(device_t dev, device_t child, struct resource *irq, void *c) +{ + int slot, ionode, hose, vector, intpin; + + intpin = pci_get_intpin(child); + slot = pci_get_slot(child); + hose = pci_get_hose(child); + ionode = hose >> 2; + hose &= 0x3; + vector = DWLPX_MVEC(ionode, hose, slot); + dwlpx_enadis_intr(vector, intpin, 0); + alpha_teardown_intr(c); + return rman_deactivate_resource(irq); } static void -dwlpx_intr(void* arg) +dwlpx_dma_init(struct dwlpx_softc *sc) +{ + u_int32_t *tbl, sgwmask, sgwbase, sgwend; + int i, lim; + + /* + * Determine size of Window C based on the amount of SGMAP + * page table SRAM available. + */ + if (sc->sgmapsz == DWLPX_SG128K) { + lim = 128 * 1024; + sgwmask = PCIA_WMASK_1G; + sgwbase = 1UL*1024UL*1024UL*1024UL; + } else { + lim = 32 * 1024; + sgwmask = PCIA_WMASK_256M; + sgwbase = 1UL*1024UL*1024UL*1024UL+3UL*256UL*1024UL*1024UL; + } + sgwend = sgwbase + (lim * 8192) - 1; + + /* + * A few notes about SGMAP-mapped DMA on the DWLPx: + * + * The DWLPx has PCIA-resident SRAM that is used for + * the SGMAP page table; there is no TLB. The DWLPA + * has room for 32K entries, yielding a total of 256M + * of sgva space. The DWLPB has 32K entries or 128K + * entries, depending on TBIT, yielding either 256M or + * 1G of sgva space. + */ + + /* + * Initialize the page table. + */ + tbl = (u_int32_t *) ALPHA_PHYS_TO_K0SEG(PCIA_SGMAP_PT + sc->sysbase); + for (i = 0; i < lim; i++) + tbl[i] = 0; + +#if 0 + /* XXX NOT DONE YET XXX */ + /* + * Initialize the SGMAP for window C: + * + * Size: 256M or 1GB + * Window base: 1GB + * SGVA base: 0 + */ + chipset.sgmap = sgmap_map_create(sgwbase, sgwend, dwlpx_sgmap_map, tbl); +#endif + + /* + * Set up DMA windows for this DWLPx. + */ + for (i = 0; i < sc->nhpc; i++) { + REGVAL(PCIA_WMASK_A(i) + sc->sysbase) = + DWLPx_DIRECT_MAPPED_WMASK; + REGVAL(PCIA_TBASE_A(i) + sc->sysbase) = 0; + REGVAL(PCIA_WBASE_A(i) + sc->sysbase) = + DWLPx_DIRECT_MAPPED_BASE | PCIA_WBASE_W_EN; + + REGVAL(PCIA_WMASK_B(i) + sc->sysbase) = 0; + REGVAL(PCIA_TBASE_B(i) + sc->sysbase) = 0; + REGVAL(PCIA_WBASE_B(i) + sc->sysbase) = 0; + + REGVAL(PCIA_WMASK_C(i) + sc->sysbase) = sgwmask; + REGVAL(PCIA_TBASE_C(i) + sc->sysbase) = 0; + REGVAL(PCIA_WBASE_C(i) + sc->sysbase) = + sgwbase | PCIA_WBASE_W_EN | PCIA_WBASE_SG_EN; + } + alpha_mb(); + + /* XXX XXX BEGIN XXX XXX */ + { /* XXX */ + alpha_XXX_dmamap_or = DWLPx_DIRECT_MAPPED_BASE; /* XXX */ + } /* XXX */ + /* XXX XXX END XXX XXX */ +} + +/* + */ + +static void +dwlpx_intr(void *arg) { #ifdef SIMOS extern void simos_intr(int); simos_intr(0); +#else + unsigned long vec = (unsigned long) arg; + if ((vec & DWLPX_VEC_EMARK) != 0) { + dwlpx_eintr(vec); + return; + } + if ((vec & DWLPX_VEC_MARK) == 0) { + panic("dwlpx_intr: bad vector %p", arg); + /* NOTREACHED */ + } + alpha_dispatch_intr(NULL, vec); #endif } -DRIVER_MODULE(dwlpx, kft, dwlpx_driver, dwlpx_devclass, 0, 0); +static void +dwlpx_eintr(unsigned long vec) +{ + device_t dev; + struct dwlpx_softc *sc; + int ionode, hosenum, i; + struct { + u_int32_t err; + u_int32_t addr; + } hpcs[NHPC]; + + ionode = (vec >> 8) & 0xf; + hosenum = (vec >> 4) & 0x7; + if (ionode >= DWLPX_NIONODE || hosenum >= DWLPX_NHOSE) { + panic("dwlpx_iointr: mangled vector 0x%lx", vec); + /* NOTREACHED */ + } + dev = dwlpxs[ionode][hosenum]; + sc = DWLPX_SOFTC(dev); + for (i = 0; i < sc->nhpc; i++) { + hpcs[i].err = REGVAL(PCIA_ERR(i) + sc->sysbase); + hpcs[i].addr = REGVAL(PCIA_FADR(i) + sc->sysbase); + } + printf("%s: node %d hose %d error interrupt\n", + device_get_nameunit(dev), ionode + 4, hosenum); + + for (i = 0; i < sc->nhpc; i++) { + if ((hpcs[i].err & PCIA_ERR_ERROR) == 0) + continue; + printf("\tHPC %d: ERR=0x%08x; DMA %s Memory, " + "Failing Address 0x%x\n", + i, hpcs[i].err, hpcs[i].addr & 0x1? "write to" : + "read from", hpcs[i].addr & ~3); + if (hpcs[i].err & PCIA_ERR_SERR_L) + printf("\t PCI device asserted SERR_L\n"); + if (hpcs[i].err & PCIA_ERR_ILAT) + printf("\t Incremental Latency Exceeded\n"); + if (hpcs[i].err & PCIA_ERR_SGPRTY) + printf("\t CPU access of SG RAM Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_ILLCSR) + printf("\t Illegal CSR Address Error\n"); + if (hpcs[i].err & PCIA_ERR_PCINXM) + printf("\t Nonexistent PCI Address Error\n"); + if (hpcs[i].err & PCIA_ERR_DSCERR) + printf("\t PCI Target Disconnect Error\n"); + if (hpcs[i].err & PCIA_ERR_ABRT) + printf("\t PCI Target Abort Error\n"); + if (hpcs[i].err & PCIA_ERR_WPRTY) + printf("\t PCI Write Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_DPERR) + printf("\t PCI Data Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_APERR) + printf("\t PCI Address Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_DFLT) + printf("\t SG Map RAM Invalid Entry Error\n"); + if (hpcs[i].err & PCIA_ERR_DPRTY) + printf("\t DMA access of SG RAM Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_DRPERR) + printf("\t DMA Read Return Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_MABRT) + printf("\t PCI Master Abort Error\n"); + if (hpcs[i].err & PCIA_ERR_CPRTY) + printf("\t CSR Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_COVR) + printf("\t CSR Overrun Error\n"); + if (hpcs[i].err & PCIA_ERR_MBPERR) + printf("\t Mailbox Parity Error\n"); + if (hpcs[i].err & PCIA_ERR_MBILI) + printf("\t Mailbox Illegal Length Error\n"); + REGVAL(PCIA_ERR(i) + sc->sysbase) = hpcs[i].err; + } +} +DRIVER_MODULE(dwlpx, kft, dwlpx_driver, dwlpx_devclass, 0, 0); |