summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/alpha/tlsb/dwlpx.c835
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);
OpenPOWER on IntegriCloud