Alpha 8200: Reinsert licence from NetBSD that should have been there
to begin with. Redo newbus attachment code so that all the DMA mapping and further pci attachment is done right. Insert config space functions (jeez- how do you do type 1 cycles?). Do the interrupt setups, etc. Basically, this is the core I/O module for 8200s, even though logically it's the 3rd level down from the nominal principle backplane bus (turbolaser). Still to be done here: S/G code isn't done yet, so we better live with 2GB or less primary memory.
This commit is contained in:
parent
a660c5056e
commit
139a060848
@ -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);
|
||||
|
||||
#define DWLPX_CFGOFF(b, s, f, r) \
|
||||
(((b) << 16) | ((s) << 11) | ((f) << 8) | (r))
|
||||
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;
|
||||
|
||||
#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))
|
||||
rvp = data = ~0;
|
||||
|
||||
#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))
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
*(u_int32_t*) (regs + PCIA_CTL(0)) = 1; /* Type1 config cycles */
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
return BUS_SETUP_INTR(parent, dev, NULL, INTR_TYPE_MISC, dwlpx_intr, 0, &intr);
|
||||
for (i = 0; i < DWLPX_MAXDEV; i++) {
|
||||
u_int16_t vec;
|
||||
int ss, hpc;
|
||||
|
||||
return 0;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user