summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2004-09-10 20:57:46 +0000
committerwpaul <wpaul@FreeBSD.org>2004-09-10 20:57:46 +0000
commita2f7a53a34c64d7c74e37510d0193de6534f692d (patch)
tree8abd9cd875f4bf82ed38f86ab5c2adc2cd593cad /sys
parent0dc3b359ffdb348ac270bc7c08f792f8604ad2b9 (diff)
downloadFreeBSD-src-a2f7a53a34c64d7c74e37510d0193de6534f692d.zip
FreeBSD-src-a2f7a53a34c64d7c74e37510d0193de6534f692d.tar.gz
Add device driver support for the VIA Networking Technologies
VT6122 gigabit ethernet chip and integrated 10/100/1000 copper PHY. The vge driver has been added to GENERIC for i386, pc98 and amd64, but not to sparc or ia64 since I don't have the ability to test it there. The vge(4) driver supports VLANs, checksum offload and jumbo frames. Also added the lge(4) and nge(4) drivers to GENERIC for i386 and pc98 since I was in the neighborhood. There's no reason to leave them out anymore.
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/conf/GENERIC3
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/mii/ciphy.c433
-rw-r--r--sys/dev/mii/ciphyreg.h351
-rw-r--r--sys/dev/mii/miidevs6
-rw-r--r--sys/dev/vge/if_vge.c2445
-rw-r--r--sys/dev/vge/if_vgereg.h697
-rw-r--r--sys/dev/vge/if_vgevar.h174
-rw-r--r--sys/i386/conf/GENERIC3
-rw-r--r--sys/modules/Makefile1
-rw-r--r--sys/modules/vge/Makefile8
-rw-r--r--sys/pc98/conf/GENERIC3
12 files changed, 4126 insertions, 0 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 75e1e56..2b94707 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -184,6 +184,8 @@ device bfe # Broadcom BCM440x 10/100 Ethernet
device bge # Broadcom BCM570xx Gigabit Ethernet
device dc # DEC/Intel 21143 and various workalikes
device fxp # Intel EtherExpress PRO/100B (82557, 82558)
+device lge # Level 1 LXT1001 gigabit ethernet
+device nge # NatSemi DP83820 gigabit ethernet
device pcn # AMD Am79C97x PCI 10/100 (precedence over 'lnc')
device re # RealTek 8139C+/8169/8169S/8110S
device rl # RealTek 8129/8139
@@ -194,6 +196,7 @@ device ste # Sundance ST201 (D-Link DFE-550TX)
device ti # Alteon Networks Tigon I/II gigabit Ethernet
device tl # Texas Instruments ThunderLAN
device tx # SMC EtherPower II (83c170 ``EPIC'')
+device vge # VIA VT612x gigabit ethernet
device vr # VIA Rhine, Rhine II
device wb # Winbond W89C840F
device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'')
diff --git a/sys/conf/files b/sys/conf/files
index 0e20689..02ddce1 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -549,6 +549,7 @@ dev/mem/memdev.c optional mem
dev/mii/amphy.c optional miibus
dev/mii/bmtphy.c optional miibus
dev/mii/brgphy.c optional miibus
+dev/mii/ciphy.c optional miibus
dev/mii/dcphy.c optional miibus pci
dev/mii/e1000phy.c optional miibus
dev/mii/exphy.c optional miibus
@@ -842,6 +843,7 @@ dev/vinum/vinumrequest.c optional vinum
dev/vinum/vinumrevive.c optional vinum
dev/vinum/vinumstate.c optional vinum
dev/vinum/vinumutil.c optional vinum
+dev/vge/if_vge.c optional vge
dev/vx/if_vx.c optional vx
dev/vx/if_vx_eisa.c optional vx eisa
dev/vx/if_vx_pci.c optional vx pci
diff --git a/sys/dev/mii/ciphy.c b/sys/dev/mii/ciphy.c
new file mode 100644
index 0000000..03a71d5
--- /dev/null
+++ b/sys/dev/mii/ciphy.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2004
+ * Bill Paul <wpaul@windriver.com>. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the Cicada CS8201 10/100/1000 copper PHY.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/bus.h>
+
+#include <machine/clock.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include "miidevs.h"
+
+#include <dev/mii/ciphyreg.h>
+
+#include "miibus_if.h"
+
+#include <machine/bus.h>
+/*
+#include <dev/vge/if_vgereg.h>
+*/
+static int ciphy_probe(device_t);
+static int ciphy_attach(device_t);
+
+static device_method_t ciphy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ciphy_probe),
+ DEVMETHOD(device_attach, ciphy_attach),
+ DEVMETHOD(device_detach, mii_phy_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ { 0, 0 }
+};
+
+static devclass_t ciphy_devclass;
+
+static driver_t ciphy_driver = {
+ "ciphy",
+ ciphy_methods,
+ sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(ciphy, miibus, ciphy_driver, ciphy_devclass, 0, 0);
+
+static int ciphy_service(struct mii_softc *, struct mii_data *, int);
+static void ciphy_status(struct mii_softc *);
+static void ciphy_reset(struct mii_softc *);
+static void ciphy_fixup(struct mii_softc *);
+
+static int
+ciphy_probe(dev)
+ device_t dev;
+{
+ struct mii_attach_args *ma;
+
+ ma = device_get_ivars(dev);
+
+ if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA &&
+ MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201) {
+ device_set_desc(dev, MII_STR_CICADA_CS8201);
+ return(0);
+ }
+
+ if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA &&
+ MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201A) {
+ device_set_desc(dev, MII_STR_CICADA_CS8201A);
+ return(0);
+ }
+
+ if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA &&
+ MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201B) {
+ device_set_desc(dev, MII_STR_CICADA_CS8201B);
+ return(0);
+ }
+
+ return(ENXIO);
+}
+
+static int
+ciphy_attach(dev)
+ device_t dev;
+{
+ struct mii_softc *sc;
+ struct mii_attach_args *ma;
+ struct mii_data *mii;
+
+ sc = device_get_softc(dev);
+ ma = device_get_ivars(dev);
+ sc->mii_dev = device_get_parent(dev);
+ mii = device_get_softc(sc->mii_dev);
+ LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
+
+ sc->mii_inst = mii->mii_instance;
+ sc->mii_phy = ma->mii_phyno;
+ sc->mii_service = ciphy_service;
+ sc->mii_pdata = mii;
+
+ sc->mii_flags |= MIIF_NOISOLATE;
+ mii->mii_instance++;
+
+ ciphy_reset(sc);
+
+ sc->mii_capabilities =
+ PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
+ if (sc->mii_capabilities & BMSR_EXTSTAT)
+ sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
+ device_printf(dev, " ");
+ mii_phy_add_media(sc);
+ printf("\n");
+
+ MIIBUS_MEDIAINIT(sc->mii_dev);
+ return(0);
+}
+
+static int
+ciphy_service(sc, mii, cmd)
+ struct mii_softc *sc;
+ struct mii_data *mii;
+ int cmd;
+{
+ struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
+ int reg, speed, gig;
+
+ switch (cmd) {
+ case MII_POLLSTAT:
+ /*
+ * If we're not polling our PHY instance, just return.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+ return (0);
+ break;
+
+ case MII_MEDIACHG:
+ /*
+ * If the media indicates a different PHY instance,
+ * isolate ourselves.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
+ reg = PHY_READ(sc, MII_BMCR);
+ PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
+ return (0);
+ }
+
+ /*
+ * If the interface is not up, don't do anything.
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ break;
+
+ ciphy_fixup(sc); /* XXX hardware bug work-around */
+
+ switch (IFM_SUBTYPE(ife->ifm_media)) {
+ case IFM_AUTO:
+#ifdef foo
+ /*
+ * If we're already in auto mode, just return.
+ */
+ if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN)
+ return (0);
+#endif
+ (void) mii_phy_auto(sc);
+ break;
+ case IFM_1000_T:
+ speed = CIPHY_S1000;
+ goto setit;
+ case IFM_100_TX:
+ speed = CIPHY_S100;
+ goto setit;
+ case IFM_10_T:
+ speed = CIPHY_S10;
+setit:
+ if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
+ speed |= CIPHY_BMCR_FDX;
+ gig = CIPHY_1000CTL_AFD;
+ } else {
+ gig = CIPHY_1000CTL_AHD;
+ }
+
+ PHY_WRITE(sc, CIPHY_MII_1000CTL, 0);
+ PHY_WRITE(sc, CIPHY_MII_BMCR, speed);
+ PHY_WRITE(sc, CIPHY_MII_ANAR, CIPHY_SEL_TYPE);
+
+ if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
+ break;
+
+ PHY_WRITE(sc, CIPHY_MII_1000CTL, gig);
+ PHY_WRITE(sc, CIPHY_MII_BMCR,
+ speed|CIPHY_BMCR_AUTOEN|CIPHY_BMCR_STARTNEG);
+
+ /*
+ * When setting the link manually, one side must
+ * be the master and the other the slave. However
+ * ifmedia doesn't give us a good way to specify
+ * this, so we fake it by using one of the LINK
+ * flags. If LINK0 is set, we program the PHY to
+ * be a master, otherwise it's a slave.
+ */
+ if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
+ PHY_WRITE(sc, CIPHY_MII_1000CTL,
+ gig|CIPHY_1000CTL_MSE|CIPHY_1000CTL_MSC);
+ } else {
+ PHY_WRITE(sc, CIPHY_MII_1000CTL,
+ gig|CIPHY_1000CTL_MSE);
+ }
+ break;
+ case IFM_NONE:
+ PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN);
+ break;
+ case IFM_100_T4:
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case MII_TICK:
+ /*
+ * If we're not currently selected, just return.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+ return (0);
+
+ /*
+ * Is the interface even up?
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ return (0);
+
+ /*
+ * Only used for autonegotiation.
+ */
+ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
+ break;
+
+ /*
+ * Check to see if we have link. If we do, we don't
+ * need to restart the autonegotiation process. Read
+ * the BMSR twice in case it's latched.
+ */
+ reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+ if (reg & BMSR_LINK)
+ break;
+
+ /*
+ * Only retry autonegotiation every 5 seconds.
+ */
+ if (++sc->mii_ticks <= 5/*10*/)
+ break;
+
+ sc->mii_ticks = 0;
+ mii_phy_auto(sc);
+ return (0);
+ }
+
+ /* Update the media status. */
+ ciphy_status(sc);
+
+ /*
+ * Callback if something changed. Note that we need to poke
+ * apply fixups for certain PHY revs.
+ */
+ if (sc->mii_media_active != mii->mii_media_active ||
+ sc->mii_media_status != mii->mii_media_status ||
+ cmd == MII_MEDIACHG) {
+ ciphy_fixup(sc);
+ }
+ mii_phy_update(sc, cmd);
+ return (0);
+}
+
+static void
+ciphy_status(sc)
+ struct mii_softc *sc;
+{
+ struct mii_data *mii = sc->mii_pdata;
+ int bmsr, bmcr;
+
+ mii->mii_media_status = IFM_AVALID;
+ mii->mii_media_active = IFM_ETHER;
+
+ bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+
+ if (bmsr & BMSR_LINK)
+ mii->mii_media_status |= IFM_ACTIVE;
+
+ bmcr = PHY_READ(sc, CIPHY_MII_BMCR);
+
+ if (bmcr & CIPHY_BMCR_LOOP)
+ mii->mii_media_active |= IFM_LOOP;
+
+ if (bmcr & CIPHY_BMCR_AUTOEN) {
+ if ((bmsr & CIPHY_BMSR_ACOMP) == 0) {
+ /* Erg, still trying, I guess... */
+ mii->mii_media_active |= IFM_NONE;
+ return;
+ }
+ }
+
+ bmsr = PHY_READ(sc, CIPHY_MII_AUXCSR);
+ switch (bmsr & CIPHY_AUXCSR_SPEED) {
+ case CIPHY_SPEED10:
+ mii->mii_media_active |= IFM_10_T;
+ break;
+ case CIPHY_SPEED100:
+ mii->mii_media_active |= IFM_100_TX;
+ break;
+ case CIPHY_SPEED1000:
+ mii->mii_media_active |= IFM_1000_T;
+ break;
+ default:
+ device_printf(sc->mii_dev, "unknown PHY speed %x\n",
+ bmsr & CIPHY_AUXCSR_SPEED);
+ break;
+ }
+
+ if (bmsr & CIPHY_AUXCSR_FDX)
+ mii->mii_media_active |= IFM_FDX;
+
+ return;
+}
+
+static void
+ciphy_reset(struct mii_softc *sc)
+{
+ mii_phy_reset(sc);
+ DELAY(1000);
+
+ return;
+}
+
+#define PHY_SETBIT(x, y, z) \
+ PHY_WRITE(x, y, (PHY_READ(x, y) | (z)))
+#define PHY_CLRBIT(x, y, z) \
+ PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z)))
+
+static void
+ciphy_fixup(struct mii_softc *sc)
+{
+ uint16_t model;
+ uint16_t status, speed;
+
+ model = MII_MODEL(PHY_READ(sc, CIPHY_MII_PHYIDR2));
+ status = PHY_READ(sc, CIPHY_MII_AUXCSR);
+ speed = status & CIPHY_AUXCSR_SPEED;
+
+ switch (model) {
+ case MII_MODEL_CICADA_CS8201:
+
+ /* Turn off "aux mode" (whatever that means) */
+ PHY_SETBIT(sc, CIPHY_MII_AUXCSR, CIPHY_AUXCSR_MDPPS);
+
+ /*
+ * Work around speed polling bug in VT3119/VT3216
+ * when using MII in full duplex mode.
+ */
+ if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) &&
+ (status & CIPHY_AUXCSR_FDX)) {
+ PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
+ } else {
+ PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
+ }
+
+ /* Enable link/activity LED blink. */
+ PHY_SETBIT(sc, CIPHY_MII_LED, CIPHY_LED_LINKACTBLINK);
+
+ break;
+
+ case MII_MODEL_CICADA_CS8201A:
+ case MII_MODEL_CICADA_CS8201B:
+
+ /*
+ * Work around speed polling bug in VT3119/VT3216
+ * when using MII in full duplex mode.
+ */
+ if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) &&
+ (status & CIPHY_AUXCSR_FDX)) {
+ PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
+ } else {
+ PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO);
+ }
+
+ break;
+ default:
+ device_printf(sc->mii_dev, "unknown CICADA PHY model %x\n",
+ model);
+ break;
+ }
+
+ return;
+}
diff --git a/sys/dev/mii/ciphyreg.h b/sys/dev/mii/ciphyreg.h
new file mode 100644
index 0000000..2822ac1
--- /dev/null
+++ b/sys/dev/mii/ciphyreg.h
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2004
+ * Bill Paul <wpaul@windriver.com>. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_MII_CIPHYREG_H_
+#define _DEV_MII_CIPHYREG_H_
+
+/*
+ * Register definitions for the Cicada CS8201 10/100/1000 gigE copper
+ * PHY, embedded within the VIA Networks VT6122 controller.
+ */
+
+/* Command register */
+#define CIPHY_MII_BMCR 0x00
+#define CIPHY_BMCR_RESET 0x8000
+#define CIPHY_BMCR_LOOP 0x4000
+#define CIPHY_BMCR_SPD0 0x2000 /* speed select, lower bit */
+#define CIPHY_BMCR_AUTOEN 0x1000 /* Autoneg enabled */
+#define CIPHY_BMCR_PDOWN 0x0800 /* Power down */
+#define CIPHY_BMCR_STARTNEG 0x0200 /* Restart autoneg */
+#define CIPHY_BMCR_FDX 0x0100 /* Duplex mode */
+#define CIPHY_BMCR_CTEST 0x0080 /* Collision test enable */
+#define CIPHY_BMCR_SPD1 0x0040 /* Speed select, upper bit */
+
+#define CIPHY_S1000 CIPHY_BMCR_SPD1 /* 1000mbps */
+#define CIPHY_S100 CIPHY_BMCR_SPD0 /* 100mpbs */
+#define CIPHY_S10 0 /* 10mbps */
+
+/* Status register */
+#define CIPHY_MII_BMSR 0x01
+#define CIPHY_BMSR_100T4 0x8000 /* 100 base T4 capable */
+#define CIPHY_BMSR_100TXFDX 0x4000 /* 100 base Tx full duplex capable */
+#define CIPHY_BMSR_100TXHDX 0x2000 /* 100 base Tx half duplex capable */
+#define CIPHY_BMSR_10TFDX 0x1000 /* 10 base T full duplex capable */
+#define CIPHY_BMSR_10THDX 0x0800 /* 10 base T half duplex capable */
+#define CIPHY_BMSR_100T2FDX 0x0400 /* 100 base T2 full duplex capable */
+#define CIPHY_BMSR_100T2HDX 0x0200 /* 100 base T2 half duplex capable */
+#define CIPHY_BMSR_EXTSTS 0x0100 /* Extended status present */
+#define CIPHY_BMSR_PRESUB 0x0040 /* Preamble surpression */
+#define CIPHY_BMSR_ACOMP 0x0020 /* Autoneg complete */
+#define CIPHY_BMSR_RFAULT 0x0010 /* Remote fault condition occured */
+#define CIPHY_BMSR_ANEG 0x0008 /* Autoneg capable */
+#define CIPHY_BMSR_LINK 0x0004 /* Link status */
+#define CIPHY_BMSR_JABBER 0x0002 /* Jabber detected */
+#define CIPHY_BMSR_EXT 0x0001 /* Extended capability */
+
+/* PHY ID registers */
+#define CIPHY_MII_PHYIDR1 0x02
+#define CIPHY_MII_PHYIDR2 0x03
+
+/* Autoneg advertisement */
+#define CIPHY_MII_ANAR 0x04
+#define CIPHY_ANAR_NP 0x8000 /* Next page */
+#define CIPHY_ANAR_RF 0x2000 /* Remote fault */
+#define CIPHY_ANAR_ASP 0x0800 /* Asymmetric Pause */
+#define CIPHY_ANAR_PC 0x0400 /* Pause capable */
+#define CIPHY_ANAR_T4 0x0200 /* local device supports 100bT4 */
+#define CIPHY_ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
+#define CIPHY_ANAR_TX 0x0080 /* local device supports 100bTx */
+#define CIPHY_ANAR_10_FD 0x0040 /* local device supports 10bT FD */
+#define CIPHY_ANAR_10 0x0020 /* local device supports 10bT */
+#define CIPHY_ANAR_SEL 0x001F /* selector field, 00001=Ethernet */
+
+/* Autoneg link partner ability */
+#define CIPHY_MII_ANLPAR 0x05
+#define CIPHY_ANLPAR_NP 0x8000 /* Next page */
+#define CIPHY_ANLPAR_ACK 0x4000 /* link partner acknowledge */
+#define CIPHY_ANLPAR_RF 0x2000 /* Remote fault */
+#define CIPHY_ANLPAR_ASP 0x0800 /* Asymmetric Pause */
+#define CIPHY_ANLPAR_PC 0x0400 /* Pause capable */
+#define CIPHY_ANLPAR_T4 0x0200 /* link partner supports 100bT4 */
+#define CIPHY_ANLPAR_TX_FD 0x0100 /* link partner supports 100bTx FD */
+#define CIPHY_ANLPAR_TX 0x0080 /* link partner supports 100bTx */
+#define CIPHY_ANLPAR_10_FD 0x0040 /* link partner supports 10bT FD */
+#define CIPHY_ANLPAR_10 0x0020 /* link partner supports 10bT */
+#define CIPHY_ANLPAR_SEL 0x001F /* selector field, 00001=Ethernet */
+
+#define CIPHY_SEL_TYPE 0x0001 /* ethernet */
+
+/* Antoneg expansion register */
+#define CIPHY_MII_ANER 0x06
+#define CIPHY_ANER_PDF 0x0010 /* Parallel detection fault */
+#define CIPHY_ANER_LPNP 0x0008 /* Link partner can next page */
+#define CIPHY_ANER_NP 0x0004 /* Local PHY can next page */
+#define CIPHY_ANER_RX 0x0002 /* Next page received */
+#define CIPHY_ANER_LPAN 0x0001 /* Link partner autoneg capable */
+
+/* Autoneg next page transmit regisyer */
+#define CIPHY_MII_NEXTP 0x07
+#define CIPHY_NEXTP_MOREP 0x8000 /* More pages to follow */
+#define CIPHY_NEXTP_MESS 0x2000 /* 1 = message page, 0 = unformatted */
+#define CIPHY_NEXTP_ACK2 0x1000 /* MAC acknowledge */
+#define CIPHY_NEXTP_TOGGLE 0x0800 /* Toggle */
+#define CIPHY_NEXTP_CODE 0x07FF /* Code bits */
+
+/* Autoneg link partner next page receive register */
+#define CIPHY_MII_NEXTP_LP 0x08
+#define CIPHY_NEXTPLP_MOREP 0x8000 /* More pages to follow */
+#define CIPHY_NEXTPLP_MESS 0x2000 /* 1 = message page, 0 = unformatted */
+#define CIPHY_NEXTPLP_ACK2 0x1000 /* MAC acknowledge */
+#define CIPHY_NEXTPLP_TOGGLE 0x0800 /* Toggle */
+#define CIPHY_NEXTPLP_CODE 0x07FF /* Code bits */
+
+/* 1000BT control register */
+#define CIPHY_MII_1000CTL 0x09
+#define CIPHY_1000CTL_TST 0xE000 /* test modes */
+#define CIPHY_1000CTL_MSE 0x1000 /* Master/Slave manual enable */
+#define CIPHY_1000CTL_MSC 0x0800 /* Master/Slave select */
+#define CIPHY_1000CTL_RD 0x0400 /* Repeater/DTE */
+#define CIPHY_1000CTL_AFD 0x0200 /* Advertise full duplex */
+#define CIPHY_1000CTL_AHD 0x0100 /* Advertise half duplex */
+
+#define CIPHY_TEST_TX_JITTER 0x2000
+#define CIPHY_TEST_TX_JITTER_MASTER_MODE 0x4000
+#define CIPHY_TEST_TX_JITTER_SLAVE_MODE 0x6000
+#define CIPHY_TEST_TX_DISTORTION 0x8000
+
+/* 1000BT status register */
+#define CIPHY_MII_1000STS 0x0A
+#define CIPHY_1000STS_MSF 0x8000 /* Master/slave fault */
+#define CIPHY_1000STS_MSR 0x4000 /* Master/slave result */
+#define CIPHY_1000STS_LRS 0x2000 /* Local receiver status */
+#define CIPHY_1000STS_RRS 0x1000 /* Remote receiver status */
+#define CIPHY_1000STS_LPFD 0x0800 /* Link partner can FD */
+#define CIPHY_1000STS_LPHD 0x0400 /* Link partner can HD */
+#define CIPHY_1000STS_IEC 0x00FF /* Idle error count */
+
+#define CIPHY_MII_EXTSTS 0x0F /* Extended status */
+#define CIPHY_EXTSTS_X_FD_CAP 0x8000 /* 1000base-X FD capable */
+#define CIPHY_EXTSTS_X_HD_CAP 0x4000 /* 1000base-X HD capable */
+#define CIPHY_EXTSTS_T_FD_CAP 0x2000 /* 1000base-T FD capable */
+#define CIPHY_EXTSTS_T_HD_CAP 0x1000 /* 1000base-T HD capable */
+
+/* 1000BT status extension register #1 */
+#define CIPHY_MII_1000STS1 0x0F
+#define CIPHY_1000STS1_1000XFDX 0x8000 /* 1000baseX FDX capable */
+#define CIPHY_1000STS1_1000XHDX 0x4000 /* 1000baseX HDX capable */
+#define CIPHY_1000STS1_1000TFDX 0x2000 /* 1000baseT FDX capable */
+#define CIPHY_1000STS1_1000THDX 0x1000 /* 1000baseT HDX capable */
+
+/* Vendor-specific PHY registers */
+
+/* 100baseTX status extention register */
+#define CIPHY_MII_100STS 0x10
+#define CIPHY_100STS_DESLCK 0x8000 /* descrambler locked */
+#define CIPHY_100STS_LKCERR 0x4000 /* lock error detected/lock lost */
+#define CIPHY_100STS_DISC 0x2000 /* disconnect state */
+#define CIPHY_100STS_LINK 0x1000 /* current link state */
+#define CIPHY_100STS_RXERR 0x0800 /* receive error detected */
+#define CIPHY_100STS_TXERR 0x0400 /* transmit error detected */
+#define CIPHY_100STS_SSDERR 0x0200 /* false carrier error detected */
+#define CIPHY_100STS_ESDERR 0x0100 /* premature end of stream error */
+
+/* 1000BT status extention register #2 */
+#define CIPHY_MII_1000STS2 0x11
+#define CIPHY_1000STS2_DESLCK 0x8000 /* descrambler locked */
+#define CIPHY_1000STS2_LKCERR 0x4000 /* lock error detected/lock lost */
+#define CIPHY_1000STS2_DISC 0x2000 /* disconnect state */
+#define CIPHY_1000STS2_LINK 0x1000 /* current link state */
+#define CIPHY_1000STS2_RXERR 0x0800 /* receive error detected */
+#define CIPHY_1000STS2_TXERR 0x0400 /* transmit error detected */
+#define CIPHY_1000STS2_SSDERR 0x0200 /* false carrier error detected */
+#define CIPHY_1000STS2_ESDERR 0x0100 /* premature end of stream error */
+#define CIPHY_1000STS2_CARREXT 0x0080 /* carrier extention err detected */
+#define CIPHY_1000STS2_BCM5400 0x0040 /* non-complient BCM5400 detected */
+
+/* Bypass control register */
+#define CIPHY_MII_BYPASS 0x12
+#define CIPHY_BYPASS_TX 0x8000 /* transmit disable */
+#define CIPHY_BYPASS_4B5B 0x4000 /* bypass the 4B5B encoder */
+#define CIPHY_BYPASS_SCRAM 0x2000 /* bypass scrambler */
+#define CIPHY_BYPASS_DSCAM 0x1000 /* bypass descrambler */
+#define CIPHY_BYPASS_PCSRX 0x0800 /* bypass PCS receive */
+#define CIPHY_BYPASS_PCSTX 0x0400 /* bypass PCS transmit */
+#define CIPHY_BYPASS_LFI 0x0200 /* bypass LFI timer */
+#define CIPHY_BYPASS_TXCLK 0x0100 /* enable transmit clock on LED4 pin */
+#define CIPHY_BYPASS_BCM5400_F 0x0080 /* force BCM5400 detect */
+#define CIPHY_BYPASS_BCM5400 0x0040 /* bypass BCM5400 detect */
+#define CIPHY_BYPASS_PAIRSWAP 0x0020 /* disable automatic pair swap */
+#define CIPHY_BYPASS_POLARITY 0x0010 /* disable polarity correction */
+#define CIPHY_BYPASS_PARALLEL 0x0008 /* parallel detect enable */
+#define CIPHY_BYPASS_PULSE 0x0004 /* disable pulse shaping filter */
+#define CIPHY_BYPASS_1000BNP 0x0002 /* disable 1000BT next page exchange */
+
+/* RX error count register */
+#define CIPHY_MII_RXERR 0x13
+
+/* False carrier sense count register */
+#define CIPHY_MII_FCSERR 0x14
+
+/* Ddisconnect error counter */
+#define CIPHY_MII_DISCERR 0x15
+
+/* 10baseT control/status register */
+#define CIPHY_MII_10BTCSR 0x16
+#define CIPHY_10BTCSR_DLIT 0x8000 /* Disable data link integrity test */
+#define CIPHY_10BTCSR_JABBER 0x4000 /* Disable jabber detect */
+#define CIPHY_10BTCSR_ECHO 0x2000 /* Disable echo mode */
+#define CIPHY_10BTCSR_SQE 0x1000 /* Disable signal quality error */
+#define CIPHY_10BTCSR_SQUENCH 0x0C00 /* Squelch control */
+#define CIPHY_10BTCSR_EOFERR 0x0100 /* End of Frame error */
+#define CIPHY_10BTCSR_DISC 0x0080 /* Disconnect status */
+#define CIPHY_10BTCSR_LINK 0x0040 /* current link state */
+#define CIPHY_10BTCSR_ITRIM 0x0038 /* current reference trim */
+#define CIPHY_10BTCSR_CSR 0x0006 /* CSR behavior control */
+
+#define CIPHY_SQUELCH_300MV 0x0000
+#define CIPHY_SQUELCH_197MV 0x0400
+#define CIPHY_SQUELCH_450MV 0x0800
+#define CIPHY_SQUELCH_RSVD 0x0C00
+
+#define CIPHY_ITRIM_PLUS2 0x0000
+#define CIPHY_ITRIM_PLUS4 0x0008
+#define CIPHY_ITRIM_PLUS6 0x0010
+#define CIPHY_ITRIM_PLUS6_ 0x0018
+#define CIPHY_ITRIM_MINUS4 0x0020
+#define CIPHY_ITRIM_MINUS4_ 0x0028
+#define CIPHY_ITRIM_MINUS2 0x0030
+#define CIPHY_ITRIM_ZERO 0x0038
+
+/* Extended PHY control register #1 */
+#define CIPHY_MII_ECTL1 0x17
+#define CIPHY_ECTL1_ACTIPHY 0x0020 /* Enable ActiPHY power saving */
+
+/* Extended PHY control register #2 */
+#define CIPHY_MII_ECTL2 0x18
+#define CIPHY_ECTL2_ERATE 0xE000 /* 10/1000 edge rate control */
+#define CIPHY_ECTL2_VTRIM 0x1C00 /* voltage reference trim */
+#define CIPHY_ECTL2_CABLELEN 0x000E /* Cable quality/length */
+#define CIPHY_ECTL2_ANALOGLOOP 0x0001 /* 1000BT analog loopback */
+
+#define CIPHY_CABLELEN_0TO10M 0x0000
+#define CIPHY_CABLELEN_10TO20M 0x0002
+#define CIPHY_CABLELEN_20TO40M 0x0004
+#define CIPHY_CABLELEN_40TO80M 0x0006
+#define CIPHY_CABLELEN_80TO100M 0x0008
+#define CIPHY_CABLELEN_100TO140M 0x000A
+#define CIPHY_CABLELEN_140TO180M 0x000C
+#define CIPHY_CABLELEN_OVER180M 0x000E
+
+/* Interrupt mask register */
+#define CIPHY_MII_IMR 0x19
+#define CIPHY_IMR_PINENABLE 0x8000 /* Interrupt pin enable */
+#define CIPHY_IMR_SPEED 0x4000 /* speed changed event */
+#define CIPHY_IMR_LINK 0x2000 /* link change/ActiPHY event */
+#define CIPHY_IMR_DPX 0x1000 /* duplex change event */
+#define CIPHY_IMR_ANEGERR 0x0800 /* autoneg error event */
+#define CIPHY_IMR_ANEGDONE 0x0400 /* autoneg done event */
+#define CIPHY_IMR_NPRX 0x0200 /* page received event */
+#define CIPHY_IMR_SYMERR 0x0100 /* symbol error event */
+#define CIPHY_IMR_LOCKERR 0x0080 /* descrambler lock lost event */
+#define CIPHY_IMR_XOVER 0x0040 /* MDI crossover change event */
+#define CIPHY_IMR_POLARITY 0x0020 /* polarity change event */
+#define CIPHY_IMR_JABBER 0x0010 /* jabber detect event */
+#define CIPHY_IMR_SSDERR 0x0008 /* false carrier detect event */
+#define CIPHY_IMR_ESDERR 0x0004 /* parallel detect error event */
+#define CIPHY_IMR_MASTERSLAVE 0x0002 /* master/slave resolve done event */
+#define CIPHY_IMR_RXERR 0x0001 /* RX error event */
+
+/* Interrupt status register */
+#define CIPHY_MII_ISR 0x1A
+#define CIPHY_ISR_IPENDING 0x8000 /* Interrupt is pending */
+#define CIPHY_ISR_SPEED 0x4000 /* speed changed event */
+#define CIPHY_ISR_LINK 0x2000 /* link change/ActiPHY event */
+#define CIPHY_ISR_DPX 0x1000 /* duplex change event */
+#define CIPHY_ISR_ANEGERR 0x0800 /* autoneg error event */
+#define CIPHY_ISR_ANEGDONE 0x0400 /* autoneg done event */
+#define CIPHY_ISR_NPRX 0x0200 /* page received event */
+#define CIPHY_ISR_SYMERR 0x0100 /* symbol error event */
+#define CIPHY_ISR_LOCKERR 0x0080 /* descrambler lock lost event */
+#define CIPHY_ISR_XOVER 0x0040 /* MDI crossover change event */
+#define CIPHY_ISR_POLARITY 0x0020 /* polarity change event */
+#define CIPHY_ISR_JABBER 0x0010 /* jabber detect event */
+#define CIPHY_ISR_SSDERR 0x0008 /* false carrier detect event */
+#define CIPHY_ISR_ESDERR 0x0004 /* parallel detect error event */
+#define CIPHY_ISR_MASTERSLAVE 0x0002 /* master/slave resolve done event */
+#define CIPHY_ISR_RXERR 0x0001 /* RX error event */
+
+/* LED control register */
+#define CIPHY_MII_LED 0x1B
+#define CIPHY_LED_LINK10FORCE 0x8000 /* Force on link10 LED */
+#define CIPHY_LED_LINK10DIS 0x4000 /* Disable link10 LED */
+#define CIPHY_LED_LINK100FORCE 0x2000 /* Force on link10 LED */
+#define CIPHY_LED_LINK100DIS 0x1000 /* Disable link100 LED */
+#define CIPHY_LED_LINK1000FORCE 0x0800 /* Force on link1000 LED */
+#define CIPHY_LED_LINK1000DIS 0x0400 /* Disable link1000 LED */
+#define CIPHY_LED_FDXFORCE 0x0200 /* Force on duplex LED */
+#define CIPHY_LED_FDXDIS 0x0100 /* Disable duplex LED */
+#define CIPHY_LED_ACTFORCE 0x0080 /* Force on activity LED */
+#define CIPHY_LED_ACTDIS 0x0040 /* Disable activity LED */
+#define CIPHY_LED_PULSE 0x0008 /* LED pulse enable */
+#define CIPHY_LED_LINKACTBLINK 0x0004 /* enable link/activity LED blink */
+#define CIPHY_LED_BLINKRATE 0x0002 /* blink rate 0=10hz, 1=5hz */
+
+/* Auxilliary control and status register */
+#define CIPHY_MII_AUXCSR 0x1C
+#define CIPHY_AUXCSR_ANEGDONE 0x8000 /* Autoneg complete */
+#define CIPHY_AUXCSR_ANEGOFF 0x4000 /* Autoneg disabled */
+#define CIPHY_AUXCSR_XOVER 0x2000 /* MDI/MDI-X crossover indication */
+#define CIPHY_AUXCSR_PAIRSWAP 0x1000 /* pair swap indication */
+#define CIPHY_AUXCSR_APOLARITY 0x0800 /* polarity inversion pair A */
+#define CIPHY_AUXCSR_BPOLARITY 0x0400 /* polarity inversion pair B */
+#define CIPHY_AUXCSR_CPOLARITY 0x0200 /* polarity inversion pair C */
+#define CIPHY_AUXCSR_DPOLARITY 0x0100 /* polarity inversion pair D */
+#define CIPHY_AUXCSR_FDX 0x0020 /* duplex 1=full, 0=half */
+#define CIPHY_AUXCSR_SPEED 0x0018 /* speed */
+#define CIPHY_AUXCSR_MDPPS 0x0004 /* No idea, not documented */
+#define CIPHY_AUXCSR_STICKYREST 0x0002 /* reset clears sticky bits */
+
+#define CIPHY_SPEED10 0x0000
+#define CIPHY_SPEED100 0x0008
+#define CIPHY_SPEED1000 0x0010
+
+/* Delay skew status register */
+#define CIPHY_MII_DSKEW 0x1D
+#define CIPHY_DSKEW_PAIRA 0x7000 /* Pair A skew in symbol times */
+#define CIPHY_DSKEW_PAIRB 0x0700 /* Pair B skew in symbol times */
+#define CIPHY_DSKEW_PAIRC 0x0070 /* Pair C skew in symbol times */
+#define CIPHY_DSKEW_PAIRD 0x0007 /* Pair D skew in symbol times */
+
+#endif /* _DEV_CIPHY_MIIREG_H_ */
diff --git a/sys/dev/mii/miidevs b/sys/dev/mii/miidevs
index 1f58417..6415c92 100644
--- a/sys/dev/mii/miidevs
+++ b/sys/dev/mii/miidevs
@@ -52,6 +52,7 @@ $FreeBSD$
oui ALTIMA 0x0010a9 Altima Communications
oui AMD 0x00001a Advanced Micro Devices
oui BROADCOM 0x001018 Broadcom Corporation
+oui CICADA 0x0003F1 Cicada Semiconductor
oui DAVICOM 0x00606e Davicom Semiconductor
oui ICS 0x00a0be Integrated Circuit Systems
oui INTEL 0x00aa00 Intel
@@ -121,6 +122,11 @@ model xxBROADCOM BCM5703 0x0016 BCM5703 10/100/1000baseTX PHY
model xxBROADCOM BCM5704 0x0019 BCM5704 10/100/1000baseTX PHY
model xxBROADCOM BCM5705 0x001a BCM5705 10/100/1000baseTX PHY
+/* Cicada Semiconductor PHYs (now owned by Vitesse?) */
+model CICADA CS8201 0x0001 Cicada CS8201 10/100/1000TX PHY
+model CICADA CS8201A 0x0020 Cicada CS8201 10/100/1000TX PHY
+model CICADA CS8201B 0x0021 Cicada CS8201 10/100/1000TX PHY
+
/* Davicom Semiconductor PHYs */
model xxDAVICOM DM9101 0x0000 DM9101 10/100 media interface
diff --git a/sys/dev/vge/if_vge.c b/sys/dev/vge/if_vge.c
new file mode 100644
index 0000000..39e4450
--- /dev/null
+++ b/sys/dev/vge/if_vge.c
@@ -0,0 +1,2445 @@
+/*
+ * Copyright (c) 2004
+ * Bill Paul <wpaul@windriver.com>. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * VIA Networking Technologies VT612x PCI gigabit ethernet NIC driver.
+ *
+ * Written by Bill Paul <wpaul@windriver.com>
+ * Senior Networking Software Engineer
+ * Wind River Systems
+ */
+
+/*
+ * The VIA Networking VT6122 is a 32bit, 33/66Mhz PCI device that
+ * combines a tri-speed ethernet MAC and PHY, with the following
+ * features:
+ *
+ * o Jumbo frame support up to 16K
+ * o Transmit and receive flow control
+ * o IPv4 checksum offload
+ * o VLAN tag insertion and stripping
+ * o TCP large send
+ * o 64-bit multicast hash table filter
+ * o 64 entry CAM filter
+ * o 16K RX FIFO and 48K TX FIFO memory
+ * o Interrupt moderation
+ *
+ * The VT6122 supports up to four transmit DMA queues. The descriptors
+ * in the transmit ring can address up to 7 data fragments; frames which
+ * span more than 7 data buffers must be coalesced, but in general the
+ * BSD TCP/IP stack rarely generates frames more than 2 or 3 fragments
+ * long. The receive descriptors address only a single buffer.
+ *
+ * There are two peculiar design issues with the VT6122. One is that
+ * receive data buffers must be aligned on a 32-bit boundary. This is
+ * not a problem where the VT6122 is used as a LOM device in x86-based
+ * systems, but on architectures that generate unaligned access traps, we
+ * have to do some copying.
+ *
+ * The other issue has to do with the way 64-bit addresses are handled.
+ * The DMA descriptors only allow you to specify 48 bits of addressing
+ * information. The remaining 16 bits are specified using one of the
+ * I/O registers. If you only have a 32-bit system, then this isn't
+ * an issue, but if you have a 64-bit system and more than 4GB of
+ * memory, you must have to make sure your network data buffers reside
+ * in the same 48-bit 'segment.'
+ *
+ * Special thanks to Ryan Fu at VIA Networking for providing documentation
+ * and sample NICs for testing.
+ */
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/taskqueue.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_vlan_var.h>
+#include <net/route.h>
+
+#include <net/bpf.h>
+
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+MODULE_DEPEND(vge, pci, 1, 1, 1);
+MODULE_DEPEND(vge, ether, 1, 1, 1);
+MODULE_DEPEND(vge, miibus, 1, 1, 1);
+
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#include <dev/vge/if_vgereg.h>
+#include <dev/vge/if_vgevar.h>
+
+#define VGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
+
+/*
+ * Various supported device vendors/types and their names.
+ */
+static struct vge_type vge_devs[] = {
+ { VIA_VENDORID, VIA_DEVICEID_61XX,
+ "VIA Networking Gigabit Ethernet" },
+ { 0, 0, NULL }
+};
+
+static int vge_probe (device_t);
+static int vge_attach (device_t);
+static int vge_detach (device_t);
+
+static int vge_encap (struct vge_softc *, struct mbuf *, int);
+
+static void vge_dma_map_addr (void *, bus_dma_segment_t *, int, int);
+static void vge_dma_map_rx_desc (void *, bus_dma_segment_t *, int,
+ bus_size_t, int);
+static void vge_dma_map_tx_desc (void *, bus_dma_segment_t *, int,
+ bus_size_t, int);
+static int vge_allocmem (device_t, struct vge_softc *);
+static int vge_newbuf (struct vge_softc *, int, struct mbuf *);
+static int vge_rx_list_init (struct vge_softc *);
+static int vge_tx_list_init (struct vge_softc *);
+#ifdef VGE_FIXUP_RX
+static __inline void vge_fixup_rx
+ (struct mbuf *);
+#endif
+static void vge_rxeof (struct vge_softc *);
+static void vge_txeof (struct vge_softc *);
+static void vge_intr (void *);
+static void vge_tick (void *);
+static void vge_tx_task (void *, int);
+static void vge_start (struct ifnet *);
+static int vge_ioctl (struct ifnet *, u_long, caddr_t);
+static void vge_init (void *);
+static void vge_stop (struct vge_softc *);
+static void vge_watchdog (struct ifnet *);
+static int vge_suspend (device_t);
+static int vge_resume (device_t);
+static void vge_shutdown (device_t);
+static int vge_ifmedia_upd (struct ifnet *);
+static void vge_ifmedia_sts (struct ifnet *, struct ifmediareq *);
+
+static void vge_eeprom_getword (struct vge_softc *, int, u_int16_t *);
+static void vge_read_eeprom (struct vge_softc *, caddr_t, int, int, int);
+
+static void vge_miipoll_start (struct vge_softc *);
+static void vge_miipoll_stop (struct vge_softc *);
+static int vge_miibus_readreg (device_t, int, int);
+static int vge_miibus_writereg (device_t, int, int, int);
+static void vge_miibus_statchg (device_t);
+
+static void vge_cam_clear (struct vge_softc *);
+static int vge_cam_set (struct vge_softc *, uint8_t *);
+#if __FreeBSD_version < 502113
+static uint32_t vge_mchash (uint8_t *);
+#endif
+static void vge_setmulti (struct vge_softc *);
+static void vge_reset (struct vge_softc *);
+
+#define VGE_PCI_LOIO 0x10
+#define VGE_PCI_LOMEM 0x14
+
+static device_method_t vge_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, vge_probe),
+ DEVMETHOD(device_attach, vge_attach),
+ DEVMETHOD(device_detach, vge_detach),
+ DEVMETHOD(device_suspend, vge_suspend),
+ DEVMETHOD(device_resume, vge_resume),
+ DEVMETHOD(device_shutdown, vge_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, vge_miibus_readreg),
+ DEVMETHOD(miibus_writereg, vge_miibus_writereg),
+ DEVMETHOD(miibus_statchg, vge_miibus_statchg),
+
+ { 0, 0 }
+};
+
+static driver_t vge_driver = {
+ "vge",
+ vge_methods,
+ sizeof(struct vge_softc)
+};
+
+static devclass_t vge_devclass;
+
+DRIVER_MODULE(vge, pci, vge_driver, vge_devclass, 0, 0);
+DRIVER_MODULE(vge, cardbus, vge_driver, vge_devclass, 0, 0);
+DRIVER_MODULE(miibus, vge, miibus_driver, miibus_devclass, 0, 0);
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static void
+vge_eeprom_getword(sc, addr, dest)
+ struct vge_softc *sc;
+ int addr;
+ u_int16_t *dest;
+{
+ register int i;
+ u_int16_t word = 0;
+
+ /*
+ * Enter EEPROM embedded programming mode. In order to
+ * access the EEPROM at all, we first have to set the
+ * EELOAD bit in the CHIPCFG2 register.
+ */
+ CSR_SETBIT_1(sc, VGE_CHIPCFG2, VGE_CHIPCFG2_EELOAD);
+ CSR_SETBIT_1(sc, VGE_EECSR, VGE_EECSR_EMBP/*|VGE_EECSR_ECS*/);
+
+ /* Select the address of the word we want to read */
+ CSR_WRITE_1(sc, VGE_EEADDR, addr);
+
+ /* Issue read command */
+ CSR_SETBIT_1(sc, VGE_EECMD, VGE_EECMD_ERD);
+
+ /* Wait for the done bit to be set. */
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ if (CSR_READ_1(sc, VGE_EECMD) & VGE_EECMD_EDONE)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT) {
+ device_printf(sc->vge_dev, "EEPROM read timed out\n");
+ *dest = 0;
+ return;
+ }
+
+ /* Read the result */
+ word = CSR_READ_2(sc, VGE_EERDDAT);
+
+ /* Turn off EEPROM access mode. */
+ CSR_CLRBIT_1(sc, VGE_EECSR, VGE_EECSR_EMBP/*|VGE_EECSR_ECS*/);
+ CSR_CLRBIT_1(sc, VGE_CHIPCFG2, VGE_CHIPCFG2_EELOAD);
+
+ *dest = word;
+
+ return;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static void
+vge_read_eeprom(sc, dest, off, cnt, swap)
+ struct vge_softc *sc;
+ caddr_t dest;
+ int off;
+ int cnt;
+ int swap;
+{
+ int i;
+ u_int16_t word = 0, *ptr;
+
+ for (i = 0; i < cnt; i++) {
+ vge_eeprom_getword(sc, off + i, &word);
+ ptr = (u_int16_t *)(dest + (i * 2));
+ if (swap)
+ *ptr = ntohs(word);
+ else
+ *ptr = word;
+ }
+}
+
+static void
+vge_miipoll_stop(sc)
+ struct vge_softc *sc;
+{
+ int i;
+
+ CSR_WRITE_1(sc, VGE_MIICMD, 0);
+
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(1);
+ if (CSR_READ_1(sc, VGE_MIISTS) & VGE_MIISTS_IIDL)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT)
+ device_printf(sc->vge_dev, "failed to idle MII autopoll\n");
+
+ return;
+}
+
+static void
+vge_miipoll_start(sc)
+ struct vge_softc *sc;
+{
+ int i;
+
+ /* First, make sure we're idle. */
+
+ CSR_WRITE_1(sc, VGE_MIICMD, 0);
+ CSR_WRITE_1(sc, VGE_MIIADDR, VGE_MIIADDR_SWMPL);
+
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(1);
+ if (CSR_READ_1(sc, VGE_MIISTS) & VGE_MIISTS_IIDL)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT) {
+ device_printf(sc->vge_dev, "failed to idle MII autopoll\n");
+ return;
+ }
+
+ /* Now enable auto poll mode. */
+
+ CSR_WRITE_1(sc, VGE_MIICMD, VGE_MIICMD_MAUTO);
+
+ /* And make sure it started. */
+
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(1);
+ if ((CSR_READ_1(sc, VGE_MIISTS) & VGE_MIISTS_IIDL) == 0)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT)
+ device_printf(sc->vge_dev, "failed to start MII autopoll\n");
+
+ return;
+}
+
+static int
+vge_miibus_readreg(dev, phy, reg)
+ device_t dev;
+ int phy, reg;
+{
+ struct vge_softc *sc;
+ int i;
+ u_int16_t rval = 0;
+
+ sc = device_get_softc(dev);
+
+ if (phy != (CSR_READ_1(sc, VGE_MIICFG) & 0x1F))
+ return(0);
+
+ VGE_LOCK(sc);
+ vge_miipoll_stop(sc);
+
+ /* Specify the register we want to read. */
+ CSR_WRITE_1(sc, VGE_MIIADDR, reg);
+
+ /* Issue read command. */
+ CSR_SETBIT_1(sc, VGE_MIICMD, VGE_MIICMD_RCMD);
+
+ /* Wait for the read command bit to self-clear. */
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(1);
+ if ((CSR_READ_1(sc, VGE_MIICMD) & VGE_MIICMD_RCMD) == 0)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT)
+ device_printf(sc->vge_dev, "MII read timed out\n");
+ else
+ rval = CSR_READ_2(sc, VGE_MIIDATA);
+
+ vge_miipoll_start(sc);
+ VGE_UNLOCK(sc);
+
+ return (rval);
+}
+
+static int
+vge_miibus_writereg(dev, phy, reg, data)
+ device_t dev;
+ int phy, reg, data;
+{
+ struct vge_softc *sc;
+ int i, rval = 0;
+
+ sc = device_get_softc(dev);
+
+ if (phy != (CSR_READ_1(sc, VGE_MIICFG) & 0x1F))
+ return(0);
+
+ VGE_LOCK(sc);
+ vge_miipoll_stop(sc);
+
+ /* Specify the register we want to write. */
+ CSR_WRITE_1(sc, VGE_MIIADDR, reg);
+
+ /* Specify the data we want to write. */
+ CSR_WRITE_2(sc, VGE_MIIDATA, data);
+
+ /* Issue write command. */
+ CSR_SETBIT_1(sc, VGE_MIICMD, VGE_MIICMD_WCMD);
+
+ /* Wait for the write command bit to self-clear. */
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(1);
+ if ((CSR_READ_1(sc, VGE_MIICMD) & VGE_MIICMD_WCMD) == 0)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT) {
+ device_printf(sc->vge_dev, "MII write timed out\n");
+ rval = EIO;
+ }
+
+ vge_miipoll_start(sc);
+ VGE_UNLOCK(sc);
+
+ return (rval);
+}
+
+static void
+vge_cam_clear(sc)
+ struct vge_softc *sc;
+{
+ int i;
+
+ /*
+ * Turn off all the mask bits. This tells the chip
+ * that none of the entries in the CAM filter are valid.
+ * desired entries will be enabled as we fill the filter in.
+ */
+
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_CAMMASK);
+ CSR_WRITE_1(sc, VGE_CAMADDR, VGE_CAMADDR_ENABLE);
+ for (i = 0; i < 8; i++)
+ CSR_WRITE_1(sc, VGE_CAM0 + i, 0);
+
+ /* Clear the VLAN filter too. */
+
+ CSR_WRITE_1(sc, VGE_CAMADDR, VGE_CAMADDR_ENABLE|VGE_CAMADDR_AVSEL|0);
+ for (i = 0; i < 8; i++)
+ CSR_WRITE_1(sc, VGE_CAM0 + i, 0);
+
+ CSR_WRITE_1(sc, VGE_CAMADDR, 0);
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_MAR);
+
+ sc->vge_camidx = 0;
+
+ return;
+}
+
+static int
+vge_cam_set(sc, addr)
+ struct vge_softc *sc;
+ uint8_t *addr;
+{
+ int i, error = 0;
+
+ if (sc->vge_camidx == VGE_CAM_MAXADDRS)
+ return(ENOSPC);
+
+ /* Select the CAM data page. */
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_CAMDATA);
+
+ /* Set the filter entry we want to update and enable writing. */
+ CSR_WRITE_1(sc, VGE_CAMADDR, VGE_CAMADDR_ENABLE|sc->vge_camidx);
+
+ /* Write the address to the CAM registers */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ CSR_WRITE_1(sc, VGE_CAM0 + i, addr[i]);
+
+ /* Issue a write command. */
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_WRITE);
+
+ /* Wake for it to clear. */
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(1);
+ if ((CSR_READ_1(sc, VGE_CAMCTL) & VGE_CAMCTL_WRITE) == 0)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT) {
+ device_printf(sc->vge_dev, "setting CAM filter failed\n");
+ error = EIO;
+ goto fail;
+ }
+
+ /* Select the CAM mask page. */
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_CAMMASK);
+
+ /* Set the mask bit that enables this filter. */
+ CSR_SETBIT_1(sc, VGE_CAM0 + (sc->vge_camidx/8),
+ 1<<(sc->vge_camidx & 7));
+
+ sc->vge_camidx++;
+
+fail:
+ /* Turn off access to CAM. */
+ CSR_WRITE_1(sc, VGE_CAMADDR, 0);
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_MAR);
+
+ return (error);
+}
+
+#if __FreeBSD_version < 502113
+static uint32_t
+vge_mchash(addr)
+ uint8_t *addr;
+{
+ uint32_t crc, carry;
+ int idx, bit;
+ uint8_t data;
+
+ /* Compute CRC for the address value. */
+ crc = 0xFFFFFFFF; /* initial value */
+
+ for (idx = 0; idx < 6; idx++) {
+ for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
+ carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01);
+ crc <<= 1;
+ if (carry)
+ crc = (crc ^ 0x04c11db6) | carry;
+ }
+ }
+
+ return(crc);
+}
+#endif
+
+/*
+ * Program the multicast filter. We use the 64-entry CAM filter
+ * for perfect filtering. If there's more than 64 multicast addresses,
+ * we use the hash filter insted.
+ */
+static void
+vge_setmulti(sc)
+ struct vge_softc *sc;
+{
+ struct ifnet *ifp;
+ int error = 0/*, h = 0*/;
+ struct ifmultiaddr *ifma;
+ u_int32_t h, hashes[2] = { 0, 0 };
+
+ ifp = &sc->arpcom.ac_if;
+
+ /* First, zot all the multicast entries. */
+ vge_cam_clear(sc);
+ CSR_WRITE_4(sc, VGE_MAR0, 0);
+ CSR_WRITE_4(sc, VGE_MAR1, 0);
+
+ /*
+ * If the user wants allmulti or promisc mode, enable reception
+ * of all multicast frames.
+ */
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ CSR_WRITE_4(sc, VGE_MAR0, 0xFFFFFFFF);
+ CSR_WRITE_4(sc, VGE_MAR1, 0xFFFFFFFF);
+ return;
+ }
+
+ /* Now program new ones */
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ error = vge_cam_set(sc,
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ if (error)
+ break;
+ }
+
+ /* If there were too many addresses, use the hash filter. */
+ if (error) {
+ vge_cam_clear(sc);
+
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+#if __FreeBSD_version < 502113
+ h = vge_mchash(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr)) >> 26;
+#else
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+#endif
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ }
+
+ CSR_WRITE_4(sc, VGE_MAR0, hashes[0]);
+ CSR_WRITE_4(sc, VGE_MAR1, hashes[1]);
+ }
+
+ return;
+}
+
+static void
+vge_reset(sc)
+ struct vge_softc *sc;
+{
+ register int i;
+
+ CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_SOFTRESET);
+
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(5);
+ if ((CSR_READ_1(sc, VGE_CRS1) & VGE_CR1_SOFTRESET) == 0)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT) {
+ device_printf(sc->vge_dev, "soft reset timed out");
+ CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_STOP_FORCE);
+ DELAY(2000);
+ }
+
+ DELAY(5000);
+
+ CSR_SETBIT_1(sc, VGE_EECSR, VGE_EECSR_RELOAD);
+
+ for (i = 0; i < VGE_TIMEOUT; i++) {
+ DELAY(5);
+ if ((CSR_READ_1(sc, VGE_EECSR) & VGE_EECSR_RELOAD) == 0)
+ break;
+ }
+
+ if (i == VGE_TIMEOUT) {
+ device_printf(sc->vge_dev, "EEPROM reload timed out\n");
+ return;
+ }
+
+ CSR_CLRBIT_1(sc, VGE_CHIPCFG0, VGE_CHIPCFG0_PACPI);
+
+ return;
+}
+
+/*
+ * Probe for a VIA gigabit chip. Check the PCI vendor and device
+ * IDs against our list and return a device name if we find a match.
+ */
+static int
+vge_probe(dev)
+ device_t dev;
+{
+ struct vge_type *t;
+ struct vge_softc *sc;
+
+ t = vge_devs;
+ sc = device_get_softc(dev);
+
+ while (t->vge_name != NULL) {
+ if ((pci_get_vendor(dev) == t->vge_vid) &&
+ (pci_get_device(dev) == t->vge_did)) {
+ device_set_desc(dev, t->vge_name);
+ return (0);
+ }
+ t++;
+ }
+
+ return (ENXIO);
+}
+
+static void
+vge_dma_map_rx_desc(arg, segs, nseg, mapsize, error)
+ void *arg;
+ bus_dma_segment_t *segs;
+ int nseg;
+ bus_size_t mapsize;
+ int error;
+{
+
+ struct vge_dmaload_arg *ctx;
+ struct vge_rx_desc *d = NULL;
+
+ if (error)
+ return;
+
+ ctx = arg;
+
+ /* Signal error to caller if there's too many segments */
+ if (nseg > ctx->vge_maxsegs) {
+ ctx->vge_maxsegs = 0;
+ return;
+ }
+
+ /*
+ * Map the segment array into descriptors.
+ */
+
+ d = &ctx->sc->vge_ldata.vge_rx_list[ctx->vge_idx];
+
+ /* If this descriptor is still owned by the chip, bail. */
+
+ if (le32toh(d->vge_sts) & VGE_RDSTS_OWN) {
+ device_printf(ctx->sc->vge_dev,
+ "tried to map busy descriptor\n");
+ ctx->vge_maxsegs = 0;
+ return;
+ }
+
+ d->vge_buflen = htole16(VGE_BUFLEN(segs[0].ds_len) | VGE_RXDESC_I);
+ d->vge_addrlo = htole32(VGE_ADDR_LO(segs[0].ds_addr));
+ d->vge_addrhi = htole16(VGE_ADDR_HI(segs[0].ds_addr) & 0xFFFF);
+ d->vge_sts = 0;
+ d->vge_ctl = 0;
+
+ ctx->vge_maxsegs = 1;
+
+ return;
+}
+
+static void
+vge_dma_map_tx_desc(arg, segs, nseg, mapsize, error)
+ void *arg;
+ bus_dma_segment_t *segs;
+ int nseg;
+ bus_size_t mapsize;
+ int error;
+{
+ struct vge_dmaload_arg *ctx;
+ struct vge_tx_desc *d = NULL;
+ struct vge_tx_frag *f;
+ int i = 0;
+
+ if (error)
+ return;
+
+ ctx = arg;
+
+ /* Signal error to caller if there's too many segments */
+ if (nseg > ctx->vge_maxsegs) {
+ ctx->vge_maxsegs = 0;
+ return;
+ }
+
+ /* Map the segment array into descriptors. */
+
+ d = &ctx->sc->vge_ldata.vge_tx_list[ctx->vge_idx];
+
+ /* If this descriptor is still owned by the chip, bail. */
+
+ if (le32toh(d->vge_sts) & VGE_TDSTS_OWN) {
+ ctx->vge_maxsegs = 0;
+ return;
+ }
+
+ for (i = 0; i < nseg; i++) {
+ f = &d->vge_frag[i];
+ f->vge_buflen = htole16(VGE_BUFLEN(segs[i].ds_len));
+ f->vge_addrlo = htole32(VGE_ADDR_LO(segs[i].ds_addr));
+ f->vge_addrhi = htole16(VGE_ADDR_HI(segs[i].ds_addr) & 0xFFFF);
+ }
+
+ /* Argh. This chip does not autopad short frames */
+
+ if (ctx->vge_m0->m_pkthdr.len < VGE_MIN_FRAMELEN) {
+ f = &d->vge_frag[i];
+ f->vge_buflen = htole16(VGE_BUFLEN(VGE_MIN_FRAMELEN -
+ ctx->vge_m0->m_pkthdr.len));
+ f->vge_addrlo = htole32(VGE_ADDR_LO(segs[0].ds_addr));
+ f->vge_addrhi = htole16(VGE_ADDR_HI(segs[0].ds_addr) & 0xFFFF);
+ ctx->vge_m0->m_pkthdr.len = VGE_MIN_FRAMELEN;
+ i++;
+ }
+
+ /*
+ * When telling the chip how many segments there are, we
+ * must use nsegs + 1 instead of just nsegs. Darned if I
+ * know why.
+ */
+ i++;
+
+ d->vge_sts = ctx->vge_m0->m_pkthdr.len << 16;
+ d->vge_ctl = ctx->vge_flags|(i << 28)|VGE_TD_LS_NORM;
+
+ if (ctx->vge_m0->m_pkthdr.len > ETHERMTU + ETHER_HDR_LEN)
+ d->vge_ctl |= VGE_TDCTL_JUMBO;
+
+ ctx->vge_maxsegs = nseg;
+
+ return;
+}
+
+/*
+ * Map a single buffer address.
+ */
+
+static void
+vge_dma_map_addr(arg, segs, nseg, error)
+ void *arg;
+ bus_dma_segment_t *segs;
+ int nseg;
+ int error;
+{
+ bus_addr_t *addr;
+
+ if (error)
+ return;
+
+ KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
+ addr = arg;
+ *addr = segs->ds_addr;
+
+ return;
+}
+
+static int
+vge_allocmem(dev, sc)
+ device_t dev;
+ struct vge_softc *sc;
+{
+ int error;
+ int nseg;
+ int i;
+
+ /*
+ * Allocate map for RX mbufs.
+ */
+ nseg = 32;
+ error = bus_dma_tag_create(sc->vge_parent_tag, ETHER_ALIGN, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
+ NULL, MCLBYTES * nseg, nseg, MCLBYTES, BUS_DMA_ALLOCNOW,
+ NULL, NULL, &sc->vge_ldata.vge_mtag);
+ if (error) {
+ device_printf(dev, "could not allocate dma tag\n");
+ return (ENOMEM);
+ }
+
+ /*
+ * Allocate map for TX descriptor list.
+ */
+ error = bus_dma_tag_create(sc->vge_parent_tag, VGE_RING_ALIGN,
+ 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
+ NULL, VGE_TX_LIST_SZ, 1, VGE_TX_LIST_SZ, BUS_DMA_ALLOCNOW,
+ NULL, NULL, &sc->vge_ldata.vge_tx_list_tag);
+ if (error) {
+ device_printf(dev, "could not allocate dma tag\n");
+ return (ENOMEM);
+ }
+
+ /* Allocate DMA'able memory for the TX ring */
+
+ error = bus_dmamem_alloc(sc->vge_ldata.vge_tx_list_tag,
+ (void **)&sc->vge_ldata.vge_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO,
+ &sc->vge_ldata.vge_tx_list_map);
+ if (error)
+ return (ENOMEM);
+
+ /* Load the map for the TX ring. */
+
+ error = bus_dmamap_load(sc->vge_ldata.vge_tx_list_tag,
+ sc->vge_ldata.vge_tx_list_map, sc->vge_ldata.vge_tx_list,
+ VGE_TX_LIST_SZ, vge_dma_map_addr,
+ &sc->vge_ldata.vge_tx_list_addr, BUS_DMA_NOWAIT);
+
+ /* Create DMA maps for TX buffers */
+
+ for (i = 0; i < VGE_TX_DESC_CNT; i++) {
+ error = bus_dmamap_create(sc->vge_ldata.vge_mtag, 0,
+ &sc->vge_ldata.vge_tx_dmamap[i]);
+ if (error) {
+ device_printf(dev, "can't create DMA map for TX\n");
+ return (ENOMEM);
+ }
+ }
+
+ /*
+ * Allocate map for RX descriptor list.
+ */
+ error = bus_dma_tag_create(sc->vge_parent_tag, VGE_RING_ALIGN,
+ 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL,
+ NULL, VGE_TX_LIST_SZ, 1, VGE_TX_LIST_SZ, BUS_DMA_ALLOCNOW,
+ NULL, NULL, &sc->vge_ldata.vge_rx_list_tag);
+ if (error) {
+ device_printf(dev, "could not allocate dma tag\n");
+ return (ENOMEM);
+ }
+
+ /* Allocate DMA'able memory for the RX ring */
+
+ error = bus_dmamem_alloc(sc->vge_ldata.vge_rx_list_tag,
+ (void **)&sc->vge_ldata.vge_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO,
+ &sc->vge_ldata.vge_rx_list_map);
+ if (error)
+ return (ENOMEM);
+
+ /* Load the map for the RX ring. */
+
+ error = bus_dmamap_load(sc->vge_ldata.vge_rx_list_tag,
+ sc->vge_ldata.vge_rx_list_map, sc->vge_ldata.vge_rx_list,
+ VGE_TX_LIST_SZ, vge_dma_map_addr,
+ &sc->vge_ldata.vge_rx_list_addr, BUS_DMA_NOWAIT);
+
+ /* Create DMA maps for RX buffers */
+
+ for (i = 0; i < VGE_RX_DESC_CNT; i++) {
+ error = bus_dmamap_create(sc->vge_ldata.vge_mtag, 0,
+ &sc->vge_ldata.vge_rx_dmamap[i]);
+ if (error) {
+ device_printf(dev, "can't create DMA map for RX\n");
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+vge_attach(dev)
+ device_t dev;
+{
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct vge_softc *sc;
+ struct ifnet *ifp;
+ int unit, error = 0, rid;
+
+ sc = device_get_softc(dev);
+ unit = device_get_unit(dev);
+ sc->vge_dev = dev;
+
+ mtx_init(&sc->vge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ /*
+ * Map control/status registers.
+ */
+ pci_enable_busmaster(dev);
+
+ rid = VGE_PCI_LOMEM;
+ sc->vge_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
+ 0, ~0, 1, RF_ACTIVE);
+
+ if (sc->vge_res == NULL) {
+ printf ("vge%d: couldn't map ports/memory\n", unit);
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->vge_btag = rman_get_bustag(sc->vge_res);
+ sc->vge_bhandle = rman_get_bushandle(sc->vge_res);
+
+ /* Allocate interrupt */
+ rid = 0;
+ sc->vge_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
+ 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
+
+ if (sc->vge_irq == NULL) {
+ printf("vge%d: couldn't map interrupt\n", unit);
+ error = ENXIO;
+ goto fail;
+ }
+
+ /* Reset the adapter. */
+ vge_reset(sc);
+
+ /*
+ * Get station address from the EEPROM.
+ */
+ vge_read_eeprom(sc, (caddr_t)eaddr, VGE_EE_EADDR, 3, 0);
+
+ sc->vge_unit = unit;
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+#if __FreeBSD_version < 502113
+ printf("vge%d: Ethernet address: %6D\n", unit, eaddr, ":");
+#endif
+
+ /*
+ * Allocate the parent bus DMA tag appropriate for PCI.
+ */
+#define VGE_NSEG_NEW 32
+ error = bus_dma_tag_create(NULL, /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MAXBSIZE, VGE_NSEG_NEW, /* maxsize, nsegments */
+ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->vge_parent_tag);
+ if (error)
+ goto fail;
+
+ error = vge_allocmem(dev, sc);
+
+ if (error)
+ goto fail;
+
+ /* Do MII setup */
+ if (mii_phy_probe(dev, &sc->vge_miibus,
+ vge_ifmedia_upd, vge_ifmedia_sts)) {
+ printf("vge%d: MII without any phy!\n", sc->vge_unit);
+ error = ENXIO;
+ goto fail;
+ }
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = vge_ioctl;
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
+ ifp->if_start = vge_start;
+ ifp->if_hwassist = VGE_CSUM_FEATURES;
+ ifp->if_capabilities |= IFCAP_HWCSUM|IFCAP_VLAN_HWTAGGING;
+#ifdef DEVICE_POLLING
+#ifdef IFCAP_POLLING
+ ifp->if_capabilities |= IFCAP_POLLING;
+#endif
+#endif
+ ifp->if_watchdog = vge_watchdog;
+ ifp->if_init = vge_init;
+ ifp->if_baudrate = 1000000000;
+ ifp->if_snd.ifq_maxlen = VGE_IFQ_MAXLEN;
+ ifp->if_capenable = ifp->if_capabilities;
+
+ TASK_INIT(&sc->vge_txtask, 0, vge_tx_task, ifp);
+
+ /*
+ * Call MI attach routine.
+ */
+ ether_ifattach(ifp, eaddr);
+
+ /* Hook interrupt last to avoid having to lock softc */
+ error = bus_setup_intr(dev, sc->vge_irq, INTR_TYPE_NET|INTR_MPSAFE,
+ vge_intr, sc, &sc->vge_intrhand);
+
+ if (error) {
+ printf("vge%d: couldn't set up irq\n", unit);
+ ether_ifdetach(ifp);
+ goto fail;
+ }
+
+fail:
+ if (error)
+ vge_detach(dev);
+
+ return (error);
+}
+
+/*
+ * Shutdown hardware and free up resources. This can be called any
+ * time after the mutex has been initialized. It is called in both
+ * the error case in attach and the normal detach case so it needs
+ * to be careful about only freeing resources that have actually been
+ * allocated.
+ */
+static int
+vge_detach(dev)
+ device_t dev;
+{
+ struct vge_softc *sc;
+ struct ifnet *ifp;
+ int i;
+
+ sc = device_get_softc(dev);
+ KASSERT(mtx_initialized(&sc->vge_mtx), ("vge mutex not initialized"));
+ ifp = &sc->arpcom.ac_if;
+
+ /* These should only be active if attach succeeded */
+ if (device_is_attached(dev)) {
+ vge_stop(sc);
+ /*
+ * Force off the IFF_UP flag here, in case someone
+ * still had a BPF descriptor attached to this
+ * interface. If they do, ether_ifattach() will cause
+ * the BPF code to try and clear the promisc mode
+ * flag, which will bubble down to vge_ioctl(),
+ * which will try to call vge_init() again. This will
+ * turn the NIC back on and restart the MII ticker,
+ * which will panic the system when the kernel tries
+ * to invoke the vge_tick() function that isn't there
+ * anymore.
+ */
+ ifp->if_flags &= ~IFF_UP;
+ ether_ifdetach(ifp);
+ }
+ if (sc->vge_miibus)
+ device_delete_child(dev, sc->vge_miibus);
+ bus_generic_detach(dev);
+
+ if (sc->vge_intrhand)
+ bus_teardown_intr(dev, sc->vge_irq, sc->vge_intrhand);
+ if (sc->vge_irq)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vge_irq);
+ if (sc->vge_res)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ VGE_PCI_LOMEM, sc->vge_res);
+
+ /* Unload and free the RX DMA ring memory and map */
+
+ if (sc->vge_ldata.vge_rx_list_tag) {
+ bus_dmamap_unload(sc->vge_ldata.vge_rx_list_tag,
+ sc->vge_ldata.vge_rx_list_map);
+ bus_dmamem_free(sc->vge_ldata.vge_rx_list_tag,
+ sc->vge_ldata.vge_rx_list,
+ sc->vge_ldata.vge_rx_list_map);
+ bus_dma_tag_destroy(sc->vge_ldata.vge_rx_list_tag);
+ }
+
+ /* Unload and free the TX DMA ring memory and map */
+
+ if (sc->vge_ldata.vge_tx_list_tag) {
+ bus_dmamap_unload(sc->vge_ldata.vge_tx_list_tag,
+ sc->vge_ldata.vge_tx_list_map);
+ bus_dmamem_free(sc->vge_ldata.vge_tx_list_tag,
+ sc->vge_ldata.vge_tx_list,
+ sc->vge_ldata.vge_tx_list_map);
+ bus_dma_tag_destroy(sc->vge_ldata.vge_tx_list_tag);
+ }
+
+ /* Destroy all the RX and TX buffer maps */
+
+ if (sc->vge_ldata.vge_mtag) {
+ for (i = 0; i < VGE_TX_DESC_CNT; i++)
+ bus_dmamap_destroy(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_tx_dmamap[i]);
+ for (i = 0; i < VGE_RX_DESC_CNT; i++)
+ bus_dmamap_destroy(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_rx_dmamap[i]);
+ bus_dma_tag_destroy(sc->vge_ldata.vge_mtag);
+ }
+
+ if (sc->vge_parent_tag)
+ bus_dma_tag_destroy(sc->vge_parent_tag);
+
+ VGE_UNLOCK(sc);
+ mtx_destroy(&sc->vge_mtx);
+
+ return (0);
+}
+
+static int
+vge_newbuf(sc, idx, m)
+ struct vge_softc *sc;
+ int idx;
+ struct mbuf *m;
+{
+ struct vge_dmaload_arg arg;
+ struct mbuf *n = NULL;
+ int i, error;
+
+ if (m == NULL) {
+ n = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (n == NULL)
+ return (ENOBUFS);
+ m = n;
+ } else
+ m->m_data = m->m_ext.ext_buf;
+
+
+#ifdef VGE_FIXUP_RX
+ /*
+ * This is part of an evil trick to deal with non-x86 platforms.
+ * The VIA chip requires RX buffers to be aligned on 32-bit
+ * boundaries, but that will hose non-x86 machines. To get around
+ * this, we leave some empty space at the start of each buffer
+ * and for non-x86 hosts, we copy the buffer back two bytes
+ * to achieve word alignment. This is slightly more efficient
+ * than allocating a new buffer, copying the contents, and
+ * discarding the old buffer.
+ */
+ m->m_len = m->m_pkthdr.len = MCLBYTES - VGE_ETHER_ALIGN;
+ m_adj(m, VGE_ETHER_ALIGN);
+#else
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+#endif
+
+ arg.sc = sc;
+ arg.vge_idx = idx;
+ arg.vge_maxsegs = 1;
+ arg.vge_flags = 0;
+
+ error = bus_dmamap_load_mbuf(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_rx_dmamap[idx], m, vge_dma_map_rx_desc,
+ &arg, BUS_DMA_NOWAIT);
+ if (error || arg.vge_maxsegs != 1) {
+ if (n != NULL)
+ m_freem(n);
+ return (ENOMEM);
+ }
+
+ /*
+ * Note: the manual fails to document the fact that for
+ * proper opration, the driver needs to replentish the RX
+ * DMA ring 4 descriptors at a time (rather than one at a
+ * time, like most chips). We can allocate the new buffers
+ * but we should not set the OWN bits until we're ready
+ * to hand back 4 of them in one shot.
+ */
+
+#define VGE_RXCHUNK 4
+ sc->vge_rx_consumed++;
+ if (sc->vge_rx_consumed == VGE_RXCHUNK) {
+ for (i = idx; i != idx - sc->vge_rx_consumed; i--)
+ sc->vge_ldata.vge_rx_list[i].vge_sts |=
+ htole32(VGE_RDSTS_OWN);
+ sc->vge_rx_consumed = 0;
+ }
+
+ sc->vge_ldata.vge_rx_mbuf[idx] = m;
+
+ bus_dmamap_sync(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_rx_dmamap[idx],
+ BUS_DMASYNC_PREREAD);
+
+ return (0);
+}
+
+static int
+vge_tx_list_init(sc)
+ struct vge_softc *sc;
+{
+ bzero ((char *)sc->vge_ldata.vge_tx_list, VGE_TX_LIST_SZ);
+ bzero ((char *)&sc->vge_ldata.vge_tx_mbuf,
+ (VGE_TX_DESC_CNT * sizeof(struct mbuf *)));
+
+ bus_dmamap_sync(sc->vge_ldata.vge_tx_list_tag,
+ sc->vge_ldata.vge_tx_list_map, BUS_DMASYNC_PREWRITE);
+ sc->vge_ldata.vge_tx_prodidx = 0;
+ sc->vge_ldata.vge_tx_considx = 0;
+ sc->vge_ldata.vge_tx_free = VGE_TX_DESC_CNT;
+
+ return (0);
+}
+
+static int
+vge_rx_list_init(sc)
+ struct vge_softc *sc;
+{
+ int i;
+
+ bzero ((char *)sc->vge_ldata.vge_rx_list, VGE_RX_LIST_SZ);
+ bzero ((char *)&sc->vge_ldata.vge_rx_mbuf,
+ (VGE_RX_DESC_CNT * sizeof(struct mbuf *)));
+
+ sc->vge_rx_consumed = 0;
+
+ for (i = 0; i < VGE_RX_DESC_CNT; i++) {
+ if (vge_newbuf(sc, i, NULL) == ENOBUFS)
+ return (ENOBUFS);
+ }
+
+ /* Flush the RX descriptors */
+
+ bus_dmamap_sync(sc->vge_ldata.vge_rx_list_tag,
+ sc->vge_ldata.vge_rx_list_map,
+ BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
+
+ sc->vge_ldata.vge_rx_prodidx = 0;
+ sc->vge_rx_consumed = 0;
+ sc->vge_head = sc->vge_tail = NULL;
+
+ return (0);
+}
+
+#ifdef VGE_FIXUP_RX
+static __inline void
+vge_fixup_rx(m)
+ struct mbuf *m;
+{
+ int i;
+ uint16_t *src, *dst;
+
+ src = mtod(m, uint16_t *);
+ dst = src - 1;
+
+ for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
+ *dst++ = *src++;
+
+ m->m_data -= ETHER_ALIGN;
+
+ return;
+}
+#endif
+
+/*
+ * RX handler. We support the reception of jumbo frames that have
+ * been fragmented across multiple 2K mbuf cluster buffers.
+ */
+static void
+vge_rxeof(sc)
+ struct vge_softc *sc;
+{
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int i, total_len;
+ int lim = 0;
+ struct vge_rx_desc *cur_rx;
+ u_int32_t rxstat, rxctl;
+
+ VGE_LOCK_ASSERT(sc);
+ ifp = &sc->arpcom.ac_if;
+ i = sc->vge_ldata.vge_rx_prodidx;
+
+ /* Invalidate the descriptor memory */
+
+ bus_dmamap_sync(sc->vge_ldata.vge_rx_list_tag,
+ sc->vge_ldata.vge_rx_list_map,
+ BUS_DMASYNC_POSTREAD);
+
+ while (!VGE_OWN(&sc->vge_ldata.vge_rx_list[i])) {
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_flags & IFF_POLLING) {
+ if (sc->rxcycles <= 0)
+ break;
+ sc->rxcycles--;
+ }
+#endif /* DEVICE_POLLING */
+
+ cur_rx = &sc->vge_ldata.vge_rx_list[i];
+ m = sc->vge_ldata.vge_rx_mbuf[i];
+ total_len = VGE_RXBYTES(cur_rx);
+ rxstat = le32toh(cur_rx->vge_sts);
+ rxctl = le32toh(cur_rx->vge_ctl);
+
+ /* Invalidate the RX mbuf and unload its map */
+
+ bus_dmamap_sync(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_rx_dmamap[i],
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_rx_dmamap[i]);
+
+ /*
+ * If the 'start of frame' bit is set, this indicates
+ * either the first fragment in a multi-fragment receive,
+ * or an intermediate fragment. Either way, we want to
+ * accumulate the buffers.
+ */
+ if (rxstat & VGE_RXPKT_SOF) {
+ m->m_len = MCLBYTES - VGE_ETHER_ALIGN;
+ if (sc->vge_head == NULL)
+ sc->vge_head = sc->vge_tail = m;
+ else {
+ m->m_flags &= ~M_PKTHDR;
+ sc->vge_tail->m_next = m;
+ sc->vge_tail = m;
+ }
+ vge_newbuf(sc, i, NULL);
+ VGE_RX_DESC_INC(i);
+ continue;
+ }
+
+ /*
+ * Bad/error frames will have the RXOK bit cleared.
+ * However, there's one error case we want to allow:
+ * if a VLAN tagged frame arrives and the chip can't
+ * match it against the CAM filter, it considers this
+ * a 'VLAN CAM filter miss' and clears the 'RXOK' bit.
+ * We don't want to drop the frame though: our VLAN
+ * filtering is done in software.
+ */
+ if (!(rxstat & VGE_RDSTS_RXOK) && !(rxstat & VGE_RDSTS_VIDM)
+ && !(rxstat & VGE_RDSTS_CSUMERR)) {
+ ifp->if_ierrors++;
+ /*
+ * If this is part of a multi-fragment packet,
+ * discard all the pieces.
+ */
+ if (sc->vge_head != NULL) {
+ m_freem(sc->vge_head);
+ sc->vge_head = sc->vge_tail = NULL;
+ }
+ vge_newbuf(sc, i, m);
+ VGE_RX_DESC_INC(i);
+ continue;
+ }
+
+ /*
+ * If allocating a replacement mbuf fails,
+ * reload the current one.
+ */
+
+ if (vge_newbuf(sc, i, NULL)) {
+ ifp->if_ierrors++;
+ if (sc->vge_head != NULL) {
+ m_freem(sc->vge_head);
+ sc->vge_head = sc->vge_tail = NULL;
+ }
+ vge_newbuf(sc, i, m);
+ VGE_RX_DESC_INC(i);
+ continue;
+ }
+
+ VGE_RX_DESC_INC(i);
+
+ if (sc->vge_head != NULL) {
+ m->m_len = total_len % (MCLBYTES - VGE_ETHER_ALIGN);
+ /*
+ * Special case: if there's 4 bytes or less
+ * in this buffer, the mbuf can be discarded:
+ * the last 4 bytes is the CRC, which we don't
+ * care about anyway.
+ */
+ if (m->m_len <= ETHER_CRC_LEN) {
+ sc->vge_tail->m_len -=
+ (ETHER_CRC_LEN - m->m_len);
+ m_freem(m);
+ } else {
+ m->m_len -= ETHER_CRC_LEN;
+ m->m_flags &= ~M_PKTHDR;
+ sc->vge_tail->m_next = m;
+ }
+ m = sc->vge_head;
+ sc->vge_head = sc->vge_tail = NULL;
+ m->m_pkthdr.len = total_len - ETHER_CRC_LEN;
+ } else
+ m->m_pkthdr.len = m->m_len =
+ (total_len - ETHER_CRC_LEN);
+
+#ifdef VGE_FIXUP_RX
+ vge_fixup_rx(m);
+#endif
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = ifp;
+
+ /* Do RX checksumming if enabled */
+ if (ifp->if_capenable & IFCAP_RXCSUM) {
+
+ /* Check IP header checksum */
+ if (rxctl & VGE_RDCTL_IPPKT)
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
+ if (rxctl & VGE_RDCTL_IPCSUMOK)
+ m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+
+ /* Check TCP/UDP checksum */
+ if (rxctl & (VGE_RDCTL_TCPPKT|VGE_RDCTL_UDPPKT) &&
+ rxctl & VGE_RDCTL_PROTOCSUMOK) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+
+ if (rxstat & VGE_RDSTS_VTAG)
+ VLAN_INPUT_TAG(ifp, m,
+ ntohs((rxctl & VGE_RDCTL_VLANID)), continue);
+
+ VGE_UNLOCK(sc);
+ (*ifp->if_input)(ifp, m);
+ VGE_LOCK(sc);
+
+ lim++;
+ if (lim == VGE_RX_DESC_CNT)
+ break;
+
+ }
+
+ /* Flush the RX DMA ring */
+
+ bus_dmamap_sync(sc->vge_ldata.vge_rx_list_tag,
+ sc->vge_ldata.vge_rx_list_map,
+ BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
+
+ sc->vge_ldata.vge_rx_prodidx = i;
+ CSR_WRITE_2(sc, VGE_RXDESC_RESIDUECNT, lim);
+
+
+ return;
+}
+
+static void
+vge_txeof(sc)
+ struct vge_softc *sc;
+{
+ struct ifnet *ifp;
+ u_int32_t txstat;
+ int idx;
+
+ ifp = &sc->arpcom.ac_if;
+ idx = sc->vge_ldata.vge_tx_considx;
+
+ /* Invalidate the TX descriptor list */
+
+ bus_dmamap_sync(sc->vge_ldata.vge_tx_list_tag,
+ sc->vge_ldata.vge_tx_list_map,
+ BUS_DMASYNC_POSTREAD);
+
+ while (idx != sc->vge_ldata.vge_tx_prodidx) {
+
+ txstat = le32toh(sc->vge_ldata.vge_tx_list[idx].vge_sts);
+ if (txstat & VGE_TDSTS_OWN)
+ break;
+
+ m_freem(sc->vge_ldata.vge_tx_mbuf[idx]);
+ sc->vge_ldata.vge_tx_mbuf[idx] = NULL;
+ bus_dmamap_unload(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_tx_dmamap[idx]);
+ if (txstat & (VGE_TDSTS_EXCESSCOLL|VGE_TDSTS_COLL))
+ ifp->if_collisions++;
+ if (txstat & VGE_TDSTS_TXERR)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ sc->vge_ldata.vge_tx_free++;
+ VGE_TX_DESC_INC(idx);
+ }
+
+ /* No changes made to the TX ring, so no flush needed */
+
+ if (idx != sc->vge_ldata.vge_tx_considx) {
+ sc->vge_ldata.vge_tx_considx = idx;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ ifp->if_timer = 0;
+ }
+
+ /*
+ * If not all descriptors have been released reaped yet,
+ * reload the timer so that we will eventually get another
+ * interrupt that will cause us to re-enter this routine.
+ * This is done in case the transmitter has gone idle.
+ */
+ if (sc->vge_ldata.vge_tx_free != VGE_TX_DESC_CNT) {
+ CSR_WRITE_2(sc, VGE_TXQCSRS, VGE_TXQCSR_WAK0);
+ CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_TIMER0_ENABLE);
+ }
+
+ return;
+}
+
+static void
+vge_tick(xsc)
+ void *xsc;
+{
+ struct vge_softc *sc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ sc = xsc;
+ ifp = &sc->arpcom.ac_if;
+ VGE_LOCK(sc);
+ mii = device_get_softc(sc->vge_miibus);
+
+ mii_tick(mii);
+ if (sc->vge_link) {
+ if (!(mii->mii_media_status & IFM_ACTIVE)) {
+ sc->vge_link = 0;
+#ifdef LINK_STATE_UP
+ sc->arpcom.ac_if.if_link_state = LINK_STATE_UP;
+ rt_ifmsg(&(sc->arpcom.ac_if));
+#endif /* LINK_STATE_UP */
+ }
+ } else {
+ if (mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->vge_link = 1;
+#ifdef LINK_STATE_DOWN
+ sc->arpcom.ac_if.if_link_state = LINK_STATE_DOWN;
+ rt_ifmsg(&(sc->arpcom.ac_if));
+#endif /* LINK_STATE_DOWN */
+#if __FreeBSD_version < 502114
+ if (ifp->if_snd.ifq_head != NULL)
+#else
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+#endif
+ taskqueue_enqueue(taskqueue_swi,
+ &sc->vge_txtask);
+ }
+ }
+
+ VGE_UNLOCK(sc);
+
+ return;
+}
+
+#ifdef DEVICE_POLLING
+static void
+vge_poll (struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct vge_softc *sc = ifp->if_softc;
+
+ VGE_LOCK(sc);
+#ifdef IFCAP_POLLING
+ if (!(ifp->if_capenable & IFCAP_POLLING)) {
+ ether_poll_deregister(ifp);
+ cmd = POLL_DEREGISTER;
+ }
+#endif
+ if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */
+ CSR_WRITE_4(sc, VGE_IMR, VGE_INTRS);
+ CSR_WRITE_4(sc, VGE_ISR, 0xFFFFFFFF);
+ CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK);
+ goto done;
+ }
+
+ sc->rxcycles = count;
+ vge_rxeof(sc);
+ vge_txeof(sc);
+
+#if __FreeBSD_version < 502114
+ if (ifp->if_snd.ifq_head != NULL)
+#else
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+#endif
+ taskqueue_enqueue(taskqueue_swi, &sc->vge_txtask);
+
+ if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
+ u_int32_t status;
+ status = CSR_READ_4(sc, VGE_ISR);
+ if (status == 0xFFFFFFFF)
+ goto done;
+ if (status)
+ CSR_WRITE_4(sc, VGE_ISR, status);
+
+ /*
+ * XXX check behaviour on receiver stalls.
+ */
+
+ if (status & VGE_ISR_TXDMA_STALL ||
+ status & VGE_ISR_RXDMA_STALL)
+ vge_init(sc);
+
+ if (status & (VGE_ISR_RXOFLOW|VGE_ISR_RXNODESC)) {
+ vge_rxeof(sc);
+ ifp->if_ierrors++;
+ CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_RUN);
+ CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_WAK);
+ }
+ }
+done:
+ VGE_UNLOCK(sc);
+}
+#endif /* DEVICE_POLLING */
+
+static void
+vge_intr(arg)
+ void *arg;
+{
+ struct vge_softc *sc;
+ struct ifnet *ifp;
+ u_int32_t status;
+
+ sc = arg;
+
+ if (sc->suspended) {
+ return;
+ }
+
+ VGE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+
+ if (!(ifp->if_flags & IFF_UP)) {
+ VGE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_flags & IFF_POLLING)
+ goto done;
+ if (
+#ifdef IFCAP_POLLING
+ (ifp->if_capenable & IFCAP_POLLING) &&
+#endif
+ ether_poll_register(vge_poll, ifp)) { /* ok, disable interrupts */
+ CSR_WRITE_4(sc, VGE_IMR, 0);
+ CSR_WRITE_1(sc, VGE_CRC3, VGE_CR3_INT_GMSK);
+ vge_poll(ifp, 0, 1);
+ goto done;
+ }
+
+#endif /* DEVICE_POLLING */
+
+ /* Disable interrupts */
+ CSR_WRITE_1(sc, VGE_CRC3, VGE_CR3_INT_GMSK);
+
+ for (;;) {
+
+ status = CSR_READ_4(sc, VGE_ISR);
+ /* If the card has gone away the read returns 0xffff. */
+ if (status == 0xFFFFFFFF)
+ break;
+
+ if (status)
+ CSR_WRITE_4(sc, VGE_ISR, status);
+
+ if ((status & VGE_INTRS) == 0)
+ break;
+
+ if (status & (VGE_ISR_RXOK|VGE_ISR_RXOK_HIPRIO))
+ vge_rxeof(sc);
+
+ if (status & (VGE_ISR_RXOFLOW|VGE_ISR_RXNODESC)) {
+ vge_rxeof(sc);
+ CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_RUN);
+ CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_WAK);
+ }
+
+ if (status & (VGE_ISR_TXOK0|VGE_ISR_TIMER0))
+ vge_txeof(sc);
+
+ if (status & (VGE_ISR_TXDMA_STALL|VGE_ISR_RXDMA_STALL))
+ vge_init(sc);
+
+ if (status & VGE_ISR_LINKSTS)
+ vge_tick(sc);
+ }
+
+ /* Re-enable interrupts */
+ CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK);
+
+#ifdef DEVICE_POLLING
+done:
+#endif
+ VGE_UNLOCK(sc);
+
+#if __FreeBSD_version < 502114
+ if (ifp->if_snd.ifq_head != NULL)
+#else
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+#endif
+ taskqueue_enqueue(taskqueue_swi, &sc->vge_txtask);
+
+ return;
+}
+
+static int
+vge_encap(sc, m_head, idx)
+ struct vge_softc *sc;
+ struct mbuf *m_head;
+ int idx;
+{
+ struct mbuf *m_new = NULL;
+ struct vge_dmaload_arg arg;
+ bus_dmamap_t map;
+ int error;
+ struct m_tag *mtag;
+
+ if (sc->vge_ldata.vge_tx_free <= 2)
+ return (EFBIG);
+
+ arg.vge_flags = 0;
+
+ if (m_head->m_pkthdr.csum_flags & CSUM_IP)
+ arg.vge_flags |= VGE_TDCTL_IPCSUM;
+ if (m_head->m_pkthdr.csum_flags & CSUM_TCP)
+ arg.vge_flags |= VGE_TDCTL_TCPCSUM;
+ if (m_head->m_pkthdr.csum_flags & CSUM_UDP)
+ arg.vge_flags |= VGE_TDCTL_UDPCSUM;
+
+ arg.sc = sc;
+ arg.vge_idx = idx;
+ arg.vge_m0 = m_head;
+ arg.vge_maxsegs = VGE_TX_FRAGS;
+
+ map = sc->vge_ldata.vge_tx_dmamap[idx];
+ error = bus_dmamap_load_mbuf(sc->vge_ldata.vge_mtag, map,
+ m_head, vge_dma_map_tx_desc, &arg, BUS_DMA_NOWAIT);
+
+ if (error && error != EFBIG) {
+ printf("vge%d: can't map mbuf (error %d)\n",
+ sc->vge_unit, error);
+ return (ENOBUFS);
+ }
+
+ /* Too many segments to map, coalesce into a single mbuf */
+
+ if (error || arg.vge_maxsegs == 0) {
+ m_new = m_defrag(m_head, M_DONTWAIT);
+ if (m_new == NULL)
+ return (1);
+ else
+ m_head = m_new;
+
+ arg.sc = sc;
+ arg.vge_m0 = m_head;
+ arg.vge_idx = idx;
+ arg.vge_maxsegs = 1;
+
+ error = bus_dmamap_load_mbuf(sc->vge_ldata.vge_mtag, map,
+ m_head, vge_dma_map_tx_desc, &arg, BUS_DMA_NOWAIT);
+ if (error) {
+ printf("vge%d: can't map mbuf (error %d)\n",
+ sc->vge_unit, error);
+ return (EFBIG);
+ }
+ }
+
+ sc->vge_ldata.vge_tx_mbuf[idx] = m_head;
+ sc->vge_ldata.vge_tx_free--;
+
+ /*
+ * Set up hardware VLAN tagging.
+ */
+
+ mtag = VLAN_OUTPUT_TAG(&sc->arpcom.ac_if, m_head);
+ if (mtag != NULL)
+ sc->vge_ldata.vge_tx_list[idx].vge_ctl |=
+ htole32(htons(VLAN_TAG_VALUE(mtag)) | VGE_TDCTL_VTAG);
+
+ sc->vge_ldata.vge_tx_list[idx].vge_sts |= htole32(VGE_TDSTS_OWN);
+
+ return (0);
+}
+
+static void
+vge_tx_task(arg, npending)
+ void *arg;
+ int npending;
+{
+ struct ifnet *ifp;
+
+ ifp = arg;
+ vge_start(ifp);
+
+ return;
+}
+
+/*
+ * Main transmit routine.
+ */
+
+static void
+vge_start(ifp)
+ struct ifnet *ifp;
+{
+ struct vge_softc *sc;
+ struct mbuf *m_head = NULL;
+ int idx, pidx = 0;
+
+ sc = ifp->if_softc;
+ VGE_LOCK(sc);
+
+ if (!sc->vge_link || ifp->if_flags & IFF_OACTIVE) {
+ VGE_UNLOCK(sc);
+ return;
+ }
+
+#if __FreeBSD_version < 502114
+ if (ifp->if_snd.ifq_head == NULL) {
+#else
+ if (IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+#endif
+ VGE_UNLOCK(sc);
+ return;
+ }
+
+ idx = sc->vge_ldata.vge_tx_prodidx;
+
+ pidx = idx - 1;
+ if (pidx < 0)
+ pidx = VGE_TX_DESC_CNT - 1;
+
+
+ while (sc->vge_ldata.vge_tx_mbuf[idx] == NULL) {
+#if __FreeBSD_version < 502114
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+#else
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+#endif
+ if (m_head == NULL)
+ break;
+
+ if (vge_encap(sc, m_head, idx)) {
+#if __FreeBSD_version >= 502114
+ IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
+#else
+ IF_PREPEND(&ifp->if_snd, m_head);
+#endif
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ sc->vge_ldata.vge_tx_list[pidx].vge_frag[0].vge_buflen |=
+ htole16(VGE_TXDESC_Q);
+
+ pidx = idx;
+ VGE_TX_DESC_INC(idx);
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+ }
+
+ if (idx == sc->vge_ldata.vge_tx_prodidx) {
+ VGE_UNLOCK(sc);
+ return;
+ }
+
+ /* Flush the TX descriptors */
+
+ bus_dmamap_sync(sc->vge_ldata.vge_tx_list_tag,
+ sc->vge_ldata.vge_tx_list_map,
+ BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
+
+ /* Issue a transmit command. */
+ CSR_WRITE_2(sc, VGE_TXQCSRS, VGE_TXQCSR_WAK0);
+
+ sc->vge_ldata.vge_tx_prodidx = idx;
+
+ /*
+ * Use the countdown timer for interrupt moderation.
+ * 'TX done' interrupts are disabled. Instead, we reset the
+ * countdown timer, which will begin counting until it hits
+ * the value in the SSTIMER register, and then trigger an
+ * interrupt. Each time we set the TIMER0_ENABLE bit, the
+ * the timer count is reloaded. Only when the transmitter
+ * is idle will the timer hit 0 and an interrupt fire.
+ */
+ CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_TIMER0_ENABLE);
+
+ VGE_UNLOCK(sc);
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+
+ return;
+}
+
+static void
+vge_init(xsc)
+ void *xsc;
+{
+ struct vge_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mii_data *mii;
+ int i;
+
+ VGE_LOCK(sc);
+ mii = device_get_softc(sc->vge_miibus);
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ vge_stop(sc);
+ vge_reset(sc);
+
+ /*
+ * Initialize the RX and TX descriptors and mbufs.
+ */
+
+ vge_rx_list_init(sc);
+ vge_tx_list_init(sc);
+
+ /* Set our station address */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ CSR_WRITE_1(sc, VGE_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+
+ /*
+ * Set receive FIFO threshold. Also allow transmission and
+ * reception of VLAN tagged frames.
+ */
+ CSR_CLRBIT_1(sc, VGE_RXCFG, VGE_RXCFG_FIFO_THR|VGE_RXCFG_VTAGOPT);
+ CSR_SETBIT_1(sc, VGE_RXCFG, VGE_RXFIFOTHR_128BYTES|VGE_VTAG_OPT2);
+
+ /* Set DMA burst length */
+ CSR_CLRBIT_1(sc, VGE_DMACFG0, VGE_DMACFG0_BURSTLEN);
+ CSR_SETBIT_1(sc, VGE_DMACFG0, VGE_DMABURST_128);
+
+ CSR_SETBIT_1(sc, VGE_TXCFG, VGE_TXCFG_ARB_PRIO|VGE_TXCFG_NONBLK);
+
+ /* Set collision backoff algorithm */
+ CSR_CLRBIT_1(sc, VGE_CHIPCFG1, VGE_CHIPCFG1_CRANDOM|
+ VGE_CHIPCFG1_CAP|VGE_CHIPCFG1_MBA|VGE_CHIPCFG1_BAKOPT);
+ CSR_SETBIT_1(sc, VGE_CHIPCFG1, VGE_CHIPCFG1_OFSET);
+
+ /* Disable LPSEL field in priority resolution */
+ CSR_SETBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_LPSEL_DIS);
+
+ /*
+ * Load the addresses of the DMA queues into the chip.
+ * Note that we only use one transmit queue.
+ */
+
+ CSR_WRITE_4(sc, VGE_TXDESC_ADDR_LO0,
+ VGE_ADDR_LO(sc->vge_ldata.vge_tx_list_addr));
+ CSR_WRITE_2(sc, VGE_TXDESCNUM, VGE_TX_DESC_CNT - 1);
+
+ CSR_WRITE_4(sc, VGE_RXDESC_ADDR_LO,
+ VGE_ADDR_LO(sc->vge_ldata.vge_rx_list_addr));
+ CSR_WRITE_2(sc, VGE_RXDESCNUM, VGE_RX_DESC_CNT - 1);
+ CSR_WRITE_2(sc, VGE_RXDESC_RESIDUECNT, VGE_RX_DESC_CNT);
+
+ /* Enable and wake up the RX descriptor queue */
+ CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_RUN);
+ CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_WAK);
+
+ /* Enable the TX descriptor queue */
+ CSR_WRITE_2(sc, VGE_TXQCSRS, VGE_TXQCSR_RUN0);
+
+ /* Set up the receive filter -- allow large frames for VLANs. */
+ CSR_WRITE_1(sc, VGE_RXCTL, VGE_RXCTL_RX_UCAST|VGE_RXCTL_RX_GIANT);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC) {
+ CSR_SETBIT_1(sc, VGE_RXCTL, VGE_RXCTL_RX_PROMISC);
+ }
+
+ /* Set capture broadcast bit to capture broadcast frames. */
+ if (ifp->if_flags & IFF_BROADCAST) {
+ CSR_SETBIT_1(sc, VGE_RXCTL, VGE_RXCTL_RX_BCAST);
+ }
+
+ /* Set multicast bit to capture multicast frames. */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ CSR_SETBIT_1(sc, VGE_RXCTL, VGE_RXCTL_RX_MCAST);
+ }
+
+ /* Init the cam filter. */
+ vge_cam_clear(sc);
+
+ /* Init the multicast filter. */
+ vge_setmulti(sc);
+
+ /* Enable flow control */
+
+ CSR_WRITE_1(sc, VGE_CRS2, 0x8B);
+
+ /* Enable jumbo frame reception (if desired) */
+
+ /* Start the MAC. */
+ CSR_WRITE_1(sc, VGE_CRC0, VGE_CR0_STOP);
+ CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_NOPOLL);
+ CSR_WRITE_1(sc, VGE_CRS0,
+ VGE_CR0_TX_ENABLE|VGE_CR0_RX_ENABLE|VGE_CR0_START);
+
+ /*
+ * Configure one-shot timer for microsecond
+ * resulution and load it for 500 usecs.
+ */
+ CSR_SETBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_TIMER0_RES);
+ CSR_WRITE_2(sc, VGE_SSTIMER, 400);
+
+ /*
+ * Configure interrupt moderation for receive. Enable
+ * the holdoff counter and load it, and set the RX
+ * suppression count to the number of descriptors we
+ * want to allow before triggering an interrupt.
+ * The holdoff timer is in units of 20 usecs.
+ */
+
+#ifdef notyet
+ CSR_WRITE_1(sc, VGE_INTCTL1, VGE_INTCTL_TXINTSUP_DISABLE);
+ /* Select the interrupt holdoff timer page. */
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_INTHLDOFF);
+ CSR_WRITE_1(sc, VGE_INTHOLDOFF, 10); /* ~200 usecs */
+
+ /* Enable use of the holdoff timer. */
+ CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_HOLDOFF);
+ CSR_WRITE_1(sc, VGE_INTCTL1, VGE_INTCTL_SC_RELOAD);
+
+ /* Select the RX suppression threshold page. */
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_RXSUPPTHR);
+ CSR_WRITE_1(sc, VGE_RXSUPPTHR, 64); /* interrupt after 64 packets */
+
+ /* Restore the page select bits. */
+ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
+ CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_MAR);
+#endif
+
+#ifdef DEVICE_POLLING
+ /*
+ * Disable interrupts if we are polling.
+ */
+ if (ifp->if_flags & IFF_POLLING) {
+ CSR_WRITE_4(sc, VGE_IMR, 0);
+ CSR_WRITE_1(sc, VGE_CRC3, VGE_CR3_INT_GMSK);
+ } else /* otherwise ... */
+#endif /* DEVICE_POLLING */
+ {
+ /*
+ * Enable interrupts.
+ */
+ CSR_WRITE_4(sc, VGE_IMR, VGE_INTRS);
+ CSR_WRITE_4(sc, VGE_ISR, 0);
+ CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK);
+ }
+
+ mii_mediachg(mii);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ sc->vge_if_flags = 0;
+ sc->vge_link = 0;
+
+ VGE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Set media options.
+ */
+static int
+vge_ifmedia_upd(ifp)
+ struct ifnet *ifp;
+{
+ struct vge_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->vge_miibus);
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+vge_ifmedia_sts(ifp, ifmr)
+ struct ifnet *ifp;
+ struct ifmediareq *ifmr;
+{
+ struct vge_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->vge_miibus);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ return;
+}
+
+static void
+vge_miibus_statchg(dev)
+ device_t dev;
+{
+ struct vge_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia_entry *ife;
+
+ sc = device_get_softc(dev);
+ mii = device_get_softc(sc->vge_miibus);
+ ife = mii->mii_media.ifm_cur;
+
+ /*
+ * If the user manually selects a media mode, we need to turn
+ * on the forced MAC mode bit in the DIAGCTL register. If the
+ * user happens to choose a full duplex mode, we also need to
+ * set the 'force full duplex' bit. This applies only to
+ * 10Mbps and 100Mbps speeds. In autoselect mode, forced MAC
+ * mode is disabled, and in 1000baseT mode, full duplex is
+ * always implied, so we turn on the forced mode bit but leave
+ * the FDX bit cleared.
+ */
+
+ switch (IFM_SUBTYPE(ife->ifm_media)) {
+ case IFM_AUTO:
+ CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_MACFORCE);
+ CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_FDXFORCE);
+ break;
+ case IFM_1000_T:
+ CSR_SETBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_MACFORCE);
+ CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_FDXFORCE);
+ break;
+ case IFM_100_TX:
+ case IFM_10_T:
+ CSR_SETBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_MACFORCE);
+ if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
+ CSR_SETBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_FDXFORCE);
+ } else {
+ CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_FDXFORCE);
+ }
+ break;
+ default:
+ device_printf(dev, "unknown media type: %x\n",
+ IFM_SUBTYPE(ife->ifm_media));
+ break;
+ }
+
+ return;
+}
+
+static int
+vge_ioctl(ifp, command, data)
+ struct ifnet *ifp;
+ u_long command;
+ caddr_t data;
+{
+ struct vge_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *) data;
+ struct mii_data *mii;
+ int error = 0;
+
+ switch (command) {
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > VGE_JUMBO_MTU)
+ error = EINVAL;
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->vge_if_flags & IFF_PROMISC)) {
+ CSR_SETBIT_1(sc, VGE_RXCTL,
+ VGE_RXCTL_RX_PROMISC);
+ vge_setmulti(sc);
+ } else if (ifp->if_flags & IFF_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->vge_if_flags & IFF_PROMISC) {
+ CSR_CLRBIT_1(sc, VGE_RXCTL,
+ VGE_RXCTL_RX_PROMISC);
+ vge_setmulti(sc);
+ } else
+ vge_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ vge_stop(sc);
+ }
+ sc->vge_if_flags = ifp->if_flags;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ vge_setmulti(sc);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = device_get_softc(sc->vge_miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+ case SIOCSIFCAP:
+#ifdef IFCAP_POLLING
+ ifp->if_capenable &= ~(IFCAP_HWCSUM | IFCAP_POLLING);
+#else
+ ifp->if_capenable &= ~(IFCAP_HWCSUM);
+#endif
+ ifp->if_capenable |=
+#ifdef IFCAP_POLLING
+ ifr->ifr_reqcap & (IFCAP_HWCSUM | IFCAP_POLLING);
+#else
+ ifr->ifr_reqcap & (IFCAP_HWCSUM);
+#endif
+ if (ifp->if_capenable & IFCAP_TXCSUM)
+ ifp->if_hwassist = VGE_CSUM_FEATURES;
+ else
+ ifp->if_hwassist = 0;
+ if (ifp->if_flags & IFF_RUNNING)
+ vge_init(sc);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ return (error);
+}
+
+static void
+vge_watchdog(ifp)
+ struct ifnet *ifp;
+{
+ struct vge_softc *sc;
+
+ sc = ifp->if_softc;
+ VGE_LOCK(sc);
+ printf("vge%d: watchdog timeout\n", sc->vge_unit);
+ ifp->if_oerrors++;
+
+ vge_txeof(sc);
+ vge_rxeof(sc);
+
+ vge_init(sc);
+
+ VGE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+vge_stop(sc)
+ struct vge_softc *sc;
+{
+ register int i;
+ struct ifnet *ifp;
+
+ VGE_LOCK(sc);
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+#ifdef DEVICE_POLLING
+ ether_poll_deregister(ifp);
+#endif /* DEVICE_POLLING */
+
+ CSR_WRITE_1(sc, VGE_CRC3, VGE_CR3_INT_GMSK);
+ CSR_WRITE_1(sc, VGE_CRS0, VGE_CR0_STOP);
+ CSR_WRITE_4(sc, VGE_ISR, 0xFFFFFFFF);
+ CSR_WRITE_2(sc, VGE_TXQCSRC, 0xFFFF);
+ CSR_WRITE_1(sc, VGE_RXQCSRC, 0xFF);
+ CSR_WRITE_4(sc, VGE_RXDESC_ADDR_LO, 0);
+
+ if (sc->vge_head != NULL) {
+ m_freem(sc->vge_head);
+ sc->vge_head = sc->vge_tail = NULL;
+ }
+
+ /* Free the TX list buffers. */
+
+ for (i = 0; i < VGE_TX_DESC_CNT; i++) {
+ if (sc->vge_ldata.vge_tx_mbuf[i] != NULL) {
+ bus_dmamap_unload(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_tx_dmamap[i]);
+ m_freem(sc->vge_ldata.vge_tx_mbuf[i]);
+ sc->vge_ldata.vge_tx_mbuf[i] = NULL;
+ }
+ }
+
+ /* Free the RX list buffers. */
+
+ for (i = 0; i < VGE_RX_DESC_CNT; i++) {
+ if (sc->vge_ldata.vge_rx_mbuf[i] != NULL) {
+ bus_dmamap_unload(sc->vge_ldata.vge_mtag,
+ sc->vge_ldata.vge_rx_dmamap[i]);
+ m_freem(sc->vge_ldata.vge_rx_mbuf[i]);
+ sc->vge_ldata.vge_rx_mbuf[i] = NULL;
+ }
+ }
+
+ VGE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Device suspend routine. Stop the interface and save some PCI
+ * settings in case the BIOS doesn't restore them properly on
+ * resume.
+ */
+static int
+vge_suspend(dev)
+ device_t dev;
+{
+ struct vge_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ vge_stop(sc);
+
+ for (i = 0; i < 5; i++)
+ sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4);
+ sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4);
+ sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1);
+ sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1);
+ sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1);
+
+ sc->suspended = 1;
+
+ return (0);
+}
+
+/*
+ * Device resume routine. Restore some PCI settings in case the BIOS
+ * doesn't, re-enable busmastering, and restart the interface if
+ * appropriate.
+ */
+static int
+vge_resume(dev)
+ device_t dev;
+{
+ struct vge_softc *sc;
+ struct ifnet *ifp;
+ int i;
+
+ sc = device_get_softc(dev);
+ ifp = &sc->arpcom.ac_if;
+
+ /* better way to do this? */
+ for (i = 0; i < 5; i++)
+ pci_write_config(dev, PCIR_MAPS + i * 4, sc->saved_maps[i], 4);
+ pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4);
+ pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1);
+ pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1);
+ pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1);
+
+ /* reenable busmastering */
+ pci_enable_busmaster(dev);
+ pci_enable_io(dev, SYS_RES_MEMORY);
+
+ /* reinitialize interface if necessary */
+ if (ifp->if_flags & IFF_UP)
+ vge_init(sc);
+
+ sc->suspended = 0;
+
+ return (0);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static void
+vge_shutdown(dev)
+ device_t dev;
+{
+ struct vge_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ vge_stop(sc);
+}
diff --git a/sys/dev/vge/if_vgereg.h b/sys/dev/vge/if_vgereg.h
new file mode 100644
index 0000000..9b7e586
--- /dev/null
+++ b/sys/dev/vge/if_vgereg.h
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) 2004
+ * Bill Paul <wpaul@windriver.com>. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Register definitions for the VIA VT6122 gigabit ethernet controller.
+ * Definitions for the built-in copper PHY can be found in vgphy.h.
+ *
+ * The VT612x controllers have 256 bytes of register space. The
+ * manual seems to imply that the registers should all be accessed
+ * using 32-bit I/O cycles, but some of them are less than 32 bits
+ * wide. Go figure.
+ */
+
+#ifndef _IF_VGEREG_H_
+#define _IF_VGEREG_H_
+
+#define VIA_VENDORID 0x1106
+#define VIA_DEVICEID_61XX 0x3119
+
+#define VGE_PAR0 0x00 /* physical address register */
+#define VGE_PAR1 0x02
+#define VGE_PAR2 0x04
+#define VGE_RXCTL 0x06 /* RX control register */
+#define VGE_TXCTL 0x07 /* TX control register */
+#define VGE_CRS0 0x08 /* Global cmd register 0 (w to set) */
+#define VGE_CRS1 0x09 /* Global cmd register 1 (w to set) */
+#define VGE_CRS2 0x0A /* Global cmd register 2 (w to set) */
+#define VGE_CRS3 0x0B /* Global cmd register 3 (w to set) */
+#define VGE_CRC0 0x0C /* Global cmd register 0 (w to clr) */
+#define VGE_CRC1 0x0D /* Global cmd register 1 (w to clr) */
+#define VGE_CRC2 0x0E /* Global cmd register 2 (w to clr) */
+#define VGE_CRC3 0x0F /* Global cmd register 3 (w to clr) */
+#define VGE_MAR0 0x10 /* Mcast hash/CAM register 0 */
+#define VGE_MAR1 0x14 /* Mcast hash/CAM register 1 */
+#define VGE_CAM0 0x10
+#define VGE_CAM1 0x11
+#define VGE_CAM2 0x12
+#define VGE_CAM3 0x13
+#define VGE_CAM4 0x14
+#define VGE_CAM5 0x15
+#define VGE_CAM6 0x16
+#define VGE_CAM7 0x17
+#define VGE_TXDESC_HIADDR 0x18 /* Hi part of 64bit txdesc base addr */
+#define VGE_DATABUF_HIADDR 0x1D /* Hi part of 64bit data buffer addr */
+#define VGE_INTCTL0 0x20 /* interrupt control register */
+#define VGE_RXSUPPTHR 0x20
+#define VGE_TXSUPPTHR 0x20
+#define VGE_INTHOLDOFF 0x20
+#define VGE_INTCTL1 0x21 /* interrupt control register */
+#define VGE_TXHOSTERR 0x22 /* TX host error status */
+#define VGE_RXHOSTERR 0x23 /* RX host error status */
+#define VGE_ISR 0x24 /* Interrupt status register */
+#define VGE_IMR 0x28 /* Interrupt mask register */
+#define VGE_TXSTS_PORT 0x2C /* Transmit status port (???) */
+#define VGE_TXQCSRS 0x30 /* TX queue ctl/status set */
+#define VGE_RXQCSRS 0x32 /* RX queue ctl/status set */
+#define VGE_TXQCSRC 0x34 /* TX queue ctl/status clear */
+#define VGE_RXQCSRC 0x36 /* RX queue ctl/status clear */
+#define VGE_RXDESC_ADDR_LO 0x38 /* RX desc base addr (lo 32 bits) */
+#define VGE_RXDESC_CONSIDX 0x3C /* Current RX descriptor index */
+#define VGE_RXQTIMER 0x3E /* RX queue timer pend register */
+#define VGE_TXQTIMER 0x3F /* TX queue timer pend register */
+#define VGE_TXDESC_ADDR_LO0 0x40 /* TX desc0 base addr (lo 32 bits) */
+#define VGE_TXDESC_ADDR_LO1 0x44 /* TX desc1 base addr (lo 32 bits) */
+#define VGE_TXDESC_ADDR_LO2 0x48 /* TX desc2 base addr (lo 32 bits) */
+#define VGE_TXDESC_ADDR_LO3 0x4C /* TX desc3 base addr (lo 32 bits) */
+#define VGE_RXDESCNUM 0x50 /* Size of RX desc ring */
+#define VGE_TXDESCNUM 0x52 /* Size of TX desc ring */
+#define VGE_TXDESC_CONSIDX0 0x54 /* Current TX descriptor index */
+#define VGE_TXDESC_CONSIDX1 0x56 /* Current TX descriptor index */
+#define VGE_TXDESC_CONSIDX2 0x58 /* Current TX descriptor index */
+#define VGE_TXDESC_CONSIDX3 0x5A /* Current TX descriptor index */
+#define VGE_TX_PAUSE_TIMER 0x5C /* TX pause frame timer */
+#define VGE_RXDESC_RESIDUECNT 0x5E /* RX descriptor residue count */
+#define VGE_FIFOTEST0 0x60 /* FIFO test register */
+#define VGE_FIFOTEST1 0x64 /* FIFO test register */
+#define VGE_CAMADDR 0x68 /* CAM address register */
+#define VGE_CAMCTL 0x69 /* CAM control register */
+#define VGE_GFTEST 0x6A
+#define VGE_FTSCMD 0x6B
+#define VGE_MIICFG 0x6C /* MII port config register */
+#define VGE_MIISTS 0x6D /* MII port status register */
+#define VGE_PHYSTS0 0x6E /* PHY status register */
+#define VGE_PHYSTS1 0x6F /* PHY status register */
+#define VGE_MIICMD 0x70 /* MII command register */
+#define VGE_MIIADDR 0x71 /* MII address register */
+#define VGE_MIIDATA 0x72 /* MII data register */
+#define VGE_SSTIMER 0x74 /* single-shot timer */
+#define VGE_PTIMER 0x76 /* periodic timer */
+#define VGE_CHIPCFG0 0x78 /* chip config A */
+#define VGE_CHIPCFG1 0x79 /* chip config B */
+#define VGE_CHIPCFG2 0x7A /* chip config C */
+#define VGE_CHIPCFG3 0x7B /* chip config D */
+#define VGE_DMACFG0 0x7C /* DMA config 0 */
+#define VGE_DMACFG1 0x7D /* DMA config 1 */
+#define VGE_RXCFG 0x7E /* MAC RX config */
+#define VGE_TXCFG 0x7F /* MAC TX config */
+#define VGE_PWRMGMT 0x82 /* power management shadow register */
+#define VGE_PWRSTAT 0x83 /* power state shadow register */
+#define VGE_MIBCSR 0x84 /* MIB control/status register */
+#define VGE_SWEEDATA 0x85 /* EEPROM software loaded data */
+#define VGE_MIBDATA 0x88 /* MIB data register */
+#define VGE_EEWRDAT 0x8C /* EEPROM embedded write */
+#define VGE_EECSUM 0x92 /* EEPROM checksum */
+#define VGE_EECSR 0x93 /* EEPROM control/status */
+#define VGE_EERDDAT 0x94 /* EEPROM embedded read */
+#define VGE_EEADDR 0x96 /* EEPROM address */
+#define VGE_EECMD 0x97 /* EEPROM embedded command */
+#define VGE_CHIPSTRAP 0x99 /* Chip jumper strapping status */
+#define VGE_MEDIASTRAP 0x9B /* Media jumper strapping */
+#define VGE_DIAGSTS 0x9C /* Chip diagnostic status */
+#define VGE_DBGCTL 0x9E /* Chip debug control */
+#define VGE_DIAGCTL 0x9F /* Chip diagnostic control */
+#define VGE_WOLCR0S 0xA0 /* WOL0 event set */
+#define VGE_WOLCR1S 0xA1 /* WOL1 event set */
+#define VGE_PWRCFGS 0xA2 /* Power management config set */
+#define VGE_WOLCFGS 0xA3 /* WOL config set */
+#define VGE_WOLCR0C 0xA4 /* WOL0 event clear */
+#define VGE_WOLCR1C 0xA5 /* WOL1 event clear */
+#define VGE_PWRCFGC 0xA6 /* Power management config clear */
+#define VGE_WOLCFGC 0xA7 /* WOL config clear */
+#define VGE_WOLSR0S 0xA8 /* WOL status set */
+#define VGE_WOLSR1S 0xA9 /* WOL status set */
+#define VGE_WOLSR0C 0xAC /* WOL status clear */
+#define VGE_WOLSR1C 0xAD /* WOL status clear */
+#define VGE_WAKEPAT_CRC0 0xB0
+#define VGE_WAKEPAT_CRC1 0xB2
+#define VGE_WAKEPAT_CRC2 0xB4
+#define VGE_WAKEPAT_CRC3 0xB6
+#define VGE_WAKEPAT_CRC4 0xB8
+#define VGE_WAKEPAT_CRC5 0xBA
+#define VGE_WAKEPAT_CRC6 0xBC
+#define VGE_WAKEPAT_CRC7 0xBE
+#define VGE_WAKEPAT_MSK0_0 0xC0
+#define VGE_WAKEPAT_MSK0_1 0xC4
+#define VGE_WAKEPAT_MSK0_2 0xC8
+#define VGE_WAKEPAT_MSK0_3 0xCC
+#define VGE_WAKEPAT_MSK1_0 0xD0
+#define VGE_WAKEPAT_MSK1_1 0xD4
+#define VGE_WAKEPAT_MSK1_2 0xD8
+#define VGE_WAKEPAT_MSK1_3 0xDC
+#define VGE_WAKEPAT_MSK2_0 0xE0
+#define VGE_WAKEPAT_MSK2_1 0xE4
+#define VGE_WAKEPAT_MSK2_2 0xE8
+#define VGE_WAKEPAT_MSK2_3 0xEC
+#define VGE_WAKEPAT_MSK3_0 0xF0
+#define VGE_WAKEPAT_MSK3_1 0xF4
+#define VGE_WAKEPAT_MSK3_2 0xF8
+#define VGE_WAKEPAT_MSK3_3 0xFC
+
+/* Receive control register */
+
+#define VGE_RXCTL_RX_BADFRAMES 0x01 /* accept CRC error frames */
+#define VGE_RXCTL_RX_RUNT 0x02 /* accept runts */
+#define VGE_RXCTL_RX_MCAST 0x04 /* accept multicasts */
+#define VGE_RXCTL_RX_BCAST 0x08 /* accept broadcasts */
+#define VGE_RXCTL_RX_PROMISC 0x10 /* promisc mode */
+#define VGE_RXCTL_RX_GIANT 0x20 /* accept VLAN tagged frames */
+#define VGE_RXCTL_RX_UCAST 0x40 /* use perfect filtering */
+#define VGE_RXCTL_RX_SYMERR 0x80 /* accept symbol err packet */
+
+/* Transmit control register */
+
+#define VGE_TXCTL_LOOPCTL 0x03 /* loopback control */
+#define VGE_TXCTL_COLLCTL 0x0C /* collision retry control */
+
+#define VGE_TXLOOPCTL_OFF 0x00
+#define VGE_TXLOOPCTL_MAC_INTERNAL 0x01
+#define VGE_TXLOOPCTL_EXTERNAL 0x02
+
+#define VGE_TXCOLLS_NORMAL 0x00 /* one set of 16 retries */
+#define VGE_TXCOLLS_32 0x04 /* two sets of 16 retries */
+#define VGE_TXCOLLS_48 0x08 /* three sets of 16 retries */
+#define VGE_TXCOLLS_INFINITE 0x0C /* retry forever */
+
+/* Global command register 0 */
+
+#define VGE_CR0_START 0x01 /* start NIC */
+#define VGE_CR0_STOP 0x02 /* stop NIC */
+#define VGE_CR0_RX_ENABLE 0x04 /* turn on RX engine */
+#define VGE_CR0_TX_ENABLE 0x08 /* turn on TX engine */
+
+/* Global command register 1 */
+
+#define VGE_CR1_NOUCAST 0x01 /* disable unicast reception */
+#define VGE_CR1_NOPOLL 0x08 /* disable RX/TX desc polling */
+#define VGE_CR1_TIMER0_ENABLE 0x20 /* enable single shot timer */
+#define VGE_CR1_TIMER1_ENABLE 0x40 /* enable periodic timer */
+#define VGE_CR1_SOFTRESET 0x80 /* software reset */
+
+/* Global command register 2 */
+
+#define VGE_CR2_TXPAUSE_THRESH_LO 0x03 /* TX pause frame lo threshold */
+#define VGE_CR2_TXPAUSE_THRESH_HI 0x0C /* TX pause frame hi threshold */
+#define VGE_CR2_HDX_FLOWCTL_ENABLE 0x10 /* half duplex flow control */
+#define VGE_CR2_FDX_RXFLOWCTL_ENABLE 0x20 /* full duplex RX flow control */
+#define VGE_CR2_FDX_TXFLOWCTL_ENABLE 0x40 /* full duplex TX flow control */
+#define VGE_CR2_XON_ENABLE 0x80 /* 802.3x XON/XOFF flow control */
+
+/* Global command register 3 */
+
+#define VGE_CR3_INT_SWPEND 0x01 /* disable multi-level int bits */
+#define VGE_CR3_INT_GMSK 0x02 /* mask off all interrupts */
+#define VGE_CR3_INT_HOLDOFF 0x04 /* enable int hold off timer */
+#define VGE_CR3_DIAG 0x10 /* diagnostic enabled */
+#define VGE_CR3_PHYRST 0x20 /* assert PHYRSTZ */
+#define VGE_CR3_STOP_FORCE 0x40 /* force NIC to stopped state */
+
+/* Interrupt control register */
+
+#define VGE_INTCTL_SC_RELOAD 0x01 /* reload hold timer */
+#define VGE_INTCTL_HC_RELOAD 0x02 /* enable hold timer reload */
+#define VGE_INTCTL_STATUS 0x04 /* interrupt pending status */
+#define VGE_INTCTL_MASK 0x18 /* multilayer int mask */
+#define VGE_INTCTL_RXINTSUP_DISABLE 0x20 /* disable RX int supression */
+#define VGE_INTCTL_TXINTSUP_DISABLE 0x40 /* disable TX int supression */
+#define VGE_INTCTL_SOFTINT 0x80 /* request soft interrupt */
+
+#define VGE_INTMASK_LAYER0 0x00
+#define VGE_INTMASK_LAYER1 0x08
+#define VGE_INTMASK_ALL 0x10
+#define VGE_INTMASK_ALL2 0x18
+
+/* Transmit host error status register */
+
+#define VGE_TXHOSTERR_TDSTRUCT 0x01 /* bad TX desc structure */
+#define VGE_TXHOSTERR_TDFETCH_BUSERR 0x02 /* bus error on desc fetch */
+#define VGE_TXHOSTERR_TDWBACK_BUSERR 0x04 /* bus error on desc writeback */
+#define VGE_TXHOSTERR_FIFOERR 0x08 /* TX FIFO DMA bus error */
+
+/* Receive host error status register */
+
+#define VGE_RXHOSTERR_RDSTRUCT 0x01 /* bad RX desc structure */
+#define VGE_RXHOSTERR_RDFETCH_BUSERR 0x02 /* bus error on desc fetch */
+#define VGE_RXHOSTERR_RDWBACK_BUSERR 0x04 /* bus error on desc writeback */
+#define VGE_RXHOSTERR_FIFOERR 0x08 /* RX FIFO DMA bus error */
+
+/* Interrupt status register */
+
+#define VGE_ISR_RXOK_HIPRIO 0x00000001 /* hi prio RX int */
+#define VGE_ISR_TXOK_HIPRIO 0x00000002 /* hi prio TX int */
+#define VGE_ISR_RXOK 0x00000004 /* normal RX done */
+#define VGE_ISR_TXOK 0x00000008 /* combo results for next 4 bits */
+#define VGE_ISR_TXOK0 0x00000010 /* TX complete on queue 0 */
+#define VGE_ISR_TXOK1 0x00000020 /* TX complete on queue 1 */
+#define VGE_ISR_TXOK2 0x00000040 /* TX complete on queue 2 */
+#define VGE_ISR_TXOK3 0x00000080 /* TX complete on queue 3 */
+#define VGE_ISR_RXCNTOFLOW 0x00000400 /* RX packet count overflow */
+#define VGE_ISR_RXPAUSE 0x00000800 /* pause frame RX'ed */
+#define VGE_ISR_RXOFLOW 0x00001000 /* RX FIFO overflow */
+#define VGE_ISR_RXNODESC 0x00002000 /* ran out of RX descriptors */
+#define VGE_ISR_RXNODESC_WARN 0x00004000 /* running out of RX descs */
+#define VGE_ISR_LINKSTS 0x00008000 /* link status change */
+#define VGE_ISR_TIMER0 0x00010000 /* one shot timer expired */
+#define VGE_ISR_TIMER1 0x00020000 /* periodic timer expired */
+#define VGE_ISR_PWR 0x00040000 /* wake up power event */
+#define VGE_ISR_PHYINT 0x00080000 /* PHY interrupt */
+#define VGE_ISR_STOPPED 0x00100000 /* software shutdown complete */
+#define VGE_ISR_MIBOFLOW 0x00200000 /* MIB counter overflow warning */
+#define VGE_ISR_SOFTINT 0x00400000 /* software interrupt */
+#define VGE_ISR_HOLDOFF_RELOAD 0x00800000 /* reload hold timer */
+#define VGE_ISR_RXDMA_STALL 0x01000000 /* RX DMA stall */
+#define VGE_ISR_TXDMA_STALL 0x02000000 /* TX DMA STALL */
+#define VGE_ISR_ISRC0 0x10000000 /* interrupt source indication */
+#define VGE_ISR_ISRC1 0x20000000 /* interrupt source indication */
+#define VGE_ISR_ISRC2 0x40000000 /* interrupt source indication */
+#define VGE_ISR_ISRC3 0x80000000 /* interrupt source indication */
+
+#define VGE_INTRS (VGE_ISR_TXOK0|VGE_ISR_RXOK|VGE_ISR_STOPPED| \
+ VGE_ISR_RXOFLOW|VGE_ISR_PHYINT| \
+ VGE_ISR_LINKSTS|VGE_ISR_RXNODESC| \
+ VGE_ISR_RXDMA_STALL|VGE_ISR_TXDMA_STALL| \
+ VGE_ISR_MIBOFLOW|VGE_ISR_TIMER0)
+
+/* Interrupt mask register */
+
+#define VGE_IMR_RXOK_HIPRIO 0x00000001 /* hi prio RX int */
+#define VGE_IMR_TXOK_HIPRIO 0x00000002 /* hi prio TX int */
+#define VGE_IMR_RXOK 0x00000004 /* normal RX done */
+#define VGE_IMR_TXOK 0x00000008 /* combo results for next 4 bits */
+#define VGE_IMR_TXOK0 0x00000010 /* TX complete on queue 0 */
+#define VGE_IMR_TXOK1 0x00000020 /* TX complete on queue 1 */
+#define VGE_IMR_TXOK2 0x00000040 /* TX complete on queue 2 */
+#define VGE_IMR_TXOK3 0x00000080 /* TX complete on queue 3 */
+#define VGE_IMR_RXCNTOFLOW 0x00000400 /* RX packet count overflow */
+#define VGE_IMR_RXPAUSE 0x00000800 /* pause frame RX'ed */
+#define VGE_IMR_RXOFLOW 0x00001000 /* RX FIFO overflow */
+#define VGE_IMR_RXNODESC 0x00002000 /* ran out of RX descriptors */
+#define VGE_IMR_RXNODESC_WARN 0x00004000 /* running out of RX descs */
+#define VGE_IMR_LINKSTS 0x00008000 /* link status change */
+#define VGE_IMR_TIMER0 0x00010000 /* one shot timer expired */
+#define VGE_IMR_TIMER1 0x00020000 /* periodic timer expired */
+#define VGE_IMR_PWR 0x00040000 /* wake up power event */
+#define VGE_IMR_PHYINT 0x00080000 /* PHY interrupt */
+#define VGE_IMR_STOPPED 0x00100000 /* software shutdown complete */
+#define VGE_IMR_MIBOFLOW 0x00200000 /* MIB counter overflow warning */
+#define VGE_IMR_SOFTINT 0x00400000 /* software interrupt */
+#define VGE_IMR_HOLDOFF_RELOAD 0x00800000 /* reload hold timer */
+#define VGE_IMR_RXDMA_STALL 0x01000000 /* RX DMA stall */
+#define VGE_IMR_TXDMA_STALL 0x02000000 /* TX DMA STALL */
+#define VGE_IMR_ISRC0 0x10000000 /* interrupt source indication */
+#define VGE_IMR_ISRC1 0x20000000 /* interrupt source indication */
+#define VGE_IMR_ISRC2 0x40000000 /* interrupt source indication */
+#define VGE_IMR_ISRC3 0x80000000 /* interrupt source indication */
+
+/* TX descriptor queue control/status register */
+
+#define VGE_TXQCSR_RUN0 0x0001 /* Enable TX queue 0 */
+#define VGE_TXQCSR_ACT0 0x0002 /* queue 0 active indicator */
+#define VGE_TXQCSR_WAK0 0x0004 /* Wake up (poll) queue 0 */
+#define VGE_TXQCST_DEAD0 0x0008 /* queue 0 dead indicator */
+#define VGE_TXQCSR_RUN1 0x0010 /* Enable TX queue 1 */
+#define VGE_TXQCSR_ACT1 0x0020 /* queue 1 active indicator */
+#define VGE_TXQCSR_WAK1 0x0040 /* Wake up (poll) queue 1 */
+#define VGE_TXQCST_DEAD1 0x0080 /* queue 1 dead indicator */
+#define VGE_TXQCSR_RUN2 0x0100 /* Enable TX queue 2 */
+#define VGE_TXQCSR_ACT2 0x0200 /* queue 2 active indicator */
+#define VGE_TXQCSR_WAK2 0x0400 /* Wake up (poll) queue 2 */
+#define VGE_TXQCST_DEAD2 0x0800 /* queue 2 dead indicator */
+#define VGE_TXQCSR_RUN3 0x1000 /* Enable TX queue 3 */
+#define VGE_TXQCSR_ACT3 0x2000 /* queue 3 active indicator */
+#define VGE_TXQCSR_WAK3 0x4000 /* Wake up (poll) queue 3 */
+#define VGE_TXQCST_DEAD3 0x8000 /* queue 3 dead indicator */
+
+/* RX descriptor queue control/status register */
+
+#define VGE_RXQCSR_RUN 0x0001 /* Enable RX queue */
+#define VGE_RXQCSR_ACT 0x0002 /* queue active indicator */
+#define VGE_RXQCSR_WAK 0x0004 /* Wake up (poll) queue */
+#define VGE_RXQCSR_DEAD 0x0008 /* queue dead indicator */
+
+/* RX/TX queue empty interrupt delay timer register */
+
+#define VGE_QTIMER_PENDCNT 0x3F
+#define VGE_QTIMER_RESOLUTION 0xC0
+
+#define VGE_QTIMER_RES_1US 0x00
+#define VGE_QTIMER_RES_4US 0x40
+#define VGE_QTIMER_RES_16US 0x80
+#define VGE_QTIMER_RES_64US 0xC0
+
+/* CAM address register */
+
+#define VGE_CAMADDR_ADDR 0x3F /* CAM address to program */
+#define VGE_CAMADDR_AVSEL 0x40 /* 0 = address cam, 1 = VLAN cam */
+#define VGE_CAMADDR_ENABLE 0x80 /* enable CAM read/write */
+
+#define VGE_CAM_MAXADDRS 64
+
+/*
+ * CAM command register
+ * Note that the page select bits in this register affect three
+ * different things:
+ * - The behavior of the MAR0/MAR1 registers at offset 0x10 (the
+ * page select bits control whether the MAR0/MAR1 registers affect
+ * the multicast hash filter or the CAM table)
+ * - The behavior of the interrupt holdoff timer register at offset
+ * 0x20 (the page select bits allow you to set the interrupt
+ * holdoff timer, the TX interrupt supression count or the
+ * RX interrupt supression count)
+ * - The behavior the WOL pattern programming registers at offset
+ * 0xC0 (controls which pattern is set)
+ */
+
+
+#define VGE_CAMCTL_WRITE 0x04 /* CAM write command */
+#define VGE_CAMCTL_READ 0x08 /* CAM read command */
+#define VGE_CAMCTL_INTPKT_SIZ 0x10 /* select interesting pkt CAM size */
+#define VGE_CAMCTL_INTPKT_ENB 0x20 /* enable interesting packet mode */
+#define VGE_CAMCTL_PAGESEL 0xC0 /* page select */
+
+#define VGE_PAGESEL_MAR 0x00
+#define VGE_PAGESEL_CAMMASK 0x40
+#define VGE_PAGESEL_CAMDATA 0x80
+
+#define VGE_PAGESEL_INTHLDOFF 0x00
+#define VGE_PAGESEL_TXSUPPTHR 0x40
+#define VGE_PAGESEL_RXSUPPTHR 0x80
+
+#define VGE_PAGESEL_WOLPAT0 0x00
+#define VGE_PAGESEL_WOLPAT1 0x40
+
+/* MII port config register */
+
+#define VGE_MIICFG_PHYADDR 0x1F /* PHY address (internal PHY is 1) */
+#define VGE_MIICFG_MDCSPEED 0x20 /* MDC accelerate x 4 */
+#define VGE_MIICFG_POLLINT 0xC0 /* polling interval */
+
+#define VGE_MIIPOLLINT_1024 0x00
+#define VGE_MIIPOLLINT_512 0x40
+#define VGE_MIIPOLLINT_128 0x80
+#define VGE_MIIPOLLINT_64 0xC0
+
+/* MII port status register */
+
+#define VGE_MIISTS_IIDL 0x80 /* not at sofrware/timer poll cycle */
+
+/* PHY status register */
+
+#define VGE_PHYSTS_TXFLOWCAP 0x01 /* resolved TX flow control cap */
+#define VGE_PHYSTS_RXFLOWCAP 0x02 /* resolved RX flow control cap */
+#define VGE_PHYSTS_SPEED10 0x04 /* PHY in 10Mbps mode */
+#define VGE_PHYSTS_SPEED1000 0x08 /* PHY in giga mode */
+#define VGE_PHYSTS_FDX 0x10 /* PHY in full duplex mode */
+#define VGE_PHYSTS_LINK 0x40 /* link status */
+#define VGE_PHYSTS_RESETSTS 0x80 /* reset status */
+
+/* MII management command register */
+
+#define VGE_MIICMD_MDC 0x01 /* clock pin */
+#define VGE_MIICMD_MDI 0x02 /* data in pin */
+#define VGE_MIICMD_MDO 0x04 /* data out pin */
+#define VGE_MIICMD_MOUT 0x08 /* data out pin enable */
+#define VGE_MIICMD_MDP 0x10 /* enable direct programming mode */
+#define VGE_MIICMD_WCMD 0x20 /* embedded mode write */
+#define VGE_MIICMD_RCMD 0x40 /* embadded mode read */
+#define VGE_MIICMD_MAUTO 0x80 /* enable autopolling */
+
+/* MII address register */
+
+#define VGE_MIIADDR_SWMPL 0x80 /* initiate priority resolution */
+
+/* Chip config register A */
+
+#define VGE_CHIPCFG0_PACPI 0x01 /* pre-ACPI wakeup function */
+#define VGE_CHIPCFG0_ABSHDN 0x02 /* abnormal shutdown function */
+#define VGE_CHIPCFG0_GPIO1PD 0x04 /* GPIO pin enable */
+#define VGE_CHIPCFG0_SKIPTAG 0x08 /* omit 802.1p tag from CRC calc */
+#define VGE_CHIPCFG0_PHLED 0x30 /* phy LED select */
+
+/* Chip config register B */
+/* Note: some of these bits are not documented in the manual! */
+
+#define VGE_CHIPCFG1_BAKOPT 0x01
+#define VGE_CHIPCFG1_MBA 0x02
+#define VGE_CHIPCFG1_CAP 0x04
+#define VGE_CHIPCFG1_CRANDOM 0x08
+#define VGE_CHIPCFG1_OFSET 0x10
+#define VGE_CHIPCFG1_SLOTTIME 0x20 /* slot time 512/500 in giga mode */
+#define VGE_CHIPCFG1_MIIOPT 0x40
+#define VGE_CHIPCFG1_GTCKOPT 0x80
+
+/* Chip config register C */
+
+#define VGE_CHIPCFG2_EELOAD 0x80 /* enable EEPROM programming */
+
+/* Chip config register D */
+
+#define VGE_CHIPCFG3_64BIT_DAC 0x20 /* enable 64bit via DAC */
+#define VGE_CHIPCFG3_IODISABLE 0x80 /* disable I/O access mode */
+
+/* DMA config register 0 */
+
+#define VGE_DMACFG0_BURSTLEN 0x07 /* RX/TX DMA burst (in dwords) */
+
+#define VGE_DMABURST_8 0x00
+#define VGE_DMABURST_16 0x01
+#define VGE_DMABURST_32 0x02
+#define VGE_DMABURST_64 0x03
+#define VGE_DMABURST_128 0x04
+#define VGE_DMABURST_256 0x05
+#define VGE_DMABURST_STRFWD 0x07
+
+/* DMA config register 1 */
+
+#define VGE_DMACFG1_LATENB 0x01 /* Latency timer enable */
+#define VGE_DMACFG1_MWWAIT 0x02 /* insert wait on master write */
+#define VGE_DMACFG1_MRWAIT 0x04 /* insert wait on master read */
+#define VGE_DMACFG1_MRM 0x08 /* use memory read multiple */
+#define VGE_DMACFG1_PERR_DIS 0x10 /* disable parity error checking */
+#define VGE_DMACFG1_XMRL 0x20 /* disable memory read line support */
+
+/* RX MAC config register */
+
+#define VGE_RXCFG_VLANFILT 0x01 /* filter VLAN ID mismatches */
+#define VGE_RXCFG_VTAGOPT 0x06 /* VLAN tag handling */
+#define VGE_RXCFG_FIFO_LOWAT 0x08 /* RX FIFO low watermark (7QW/15QW) */
+#define VGE_RXCFG_FIFO_THR 0x30 /* RX FIFO threshold */
+#define VGE_RXCFG_ARB_PRIO 0x80 /* arbitration priority */
+
+#define VGE_VTAG_OPT0 0x00 /* TX: no tag insertion
+ RX: rx all, no tag extraction */
+
+#define VGE_VTAG_OPT1 0x02 /* TX: no tag insertion
+ RX: rx only tagged pkts, no
+ extraction */
+
+#define VGE_VTAG_OPT2 0x04 /* TX: perform tag insertion,
+ RX: rx all, extract tags */
+
+#define VGE_VTAG_OPT3 0x06 /* TX: perform tag insertion,
+ RX: rx only tagged pkts,
+ with extraction */
+
+#define VGE_RXFIFOTHR_128BYTES 0x00
+#define VGE_RXFIFOTHR_512BYTES 0x10
+#define VGE_RXFIFOTHR_1024BYTES 0x20
+#define VGE_RXFIFOTHR_STRNFWD 0x30
+
+/* TX MAC config register */
+
+#define VGE_TXCFG_SNAPOPT 0x01 /* 1 == insert VLAN tag at
+ 13th byte
+ 0 == insert VLANM tag after
+ SNAP header (21st byte) */
+#define VGE_TXCFG_NONBLK 0x02 /* priority TX/non-blocking mode */
+#define VGE_TXCFG_NONBLK_THR 0x0C /* non-blocking threshold */
+#define VGE_TXCFG_ARB_PRIO 0x80 /* arbitration priority */
+
+#define VGE_TXBLOCK_64PKTS 0x00
+#define VGE_TXBLOCK_32PKTS 0x04
+#define VGE_TXBLOCK_128PKTS 0x08
+#define VGE_TXBLOCK_8PKTS 0x0C
+
+/* EEPROM control/status register */
+
+#define VGE_EECSR_EDO 0x01 /* data out pin */
+#define VGE_EECSR_EDI 0x02 /* data in pin */
+#define VGE_EECSR_ECK 0x04 /* clock pin */
+#define VGE_EECSR_ECS 0x08 /* chip select pin */
+#define VGE_EECSR_DPM 0x10 /* direct program mode enable */
+#define VGE_EECSR_RELOAD 0x20 /* trigger reload from EEPROM */
+#define VGE_EECSR_EMBP 0x40 /* embedded program mode enable */
+
+/* EEPROM embedded command register */
+
+#define VGE_EECMD_ERD 0x01 /* EEPROM read command */
+#define VGE_EECMD_EWR 0x02 /* EEPROM write command */
+#define VGE_EECMD_EWEN 0x04 /* EEPROM write enable */
+#define VGE_EECMD_EWDIS 0x08 /* EEPROM write disable */
+#define VGE_EECMD_EDONE 0x80 /* read/write done */
+
+/* Chip operation and diagnostic control register */
+
+#define VGE_DIAGCTL_PHYINT_ENB 0x01 /* Enable PHY interrupts */
+#define VGE_DIAGCTL_TIMER0_RES 0x02 /* timer0 uSec resolution */
+#define VGE_DIAGCTL_TIMER1_RES 0x04 /* timer1 uSec resolution */
+#define VGE_DIAGCTL_LPSEL_DIS 0x08 /* disable LPSEL field */
+#define VGE_DIAGCTL_MACFORCE 0x10 /* MAC side force mode */
+#define VGE_DIAGCTL_FCRSVD 0x20 /* reserved for future fiber use */
+#define VGE_DIAGCTL_FDXFORCE 0x40 /* force full duplex mode */
+#define VGE_DIAGCTL_GMII 0x80 /* force GMII mode, otherwise MII */
+
+/* Location of station address in EEPROM */
+#define VGE_EE_EADDR 0
+
+/* DMA descriptor structures */
+
+/*
+ * Each TX DMA descriptor has a control and status word, and 7
+ * fragment address/length words. If a transmitted packet spans
+ * more than 7 fragments, it has to be coalesced.
+ */
+
+#define VGE_TX_FRAGS 7
+
+struct vge_tx_frag {
+ uint32_t vge_addrlo;
+ uint16_t vge_addrhi;
+ uint16_t vge_buflen;
+};
+
+/*
+ * The high bit in the buflen field of fragment #0 has special meaning.
+ * Normally, the chip requires the driver to issue a TX poll command
+ * for every packet that gets put in the TX DMA queue. Sometimes though,
+ * the driver might want to queue up several packets at once and just
+ * issue one transmit command to have all of them processed. In order
+ * to obtain this behavior, the special 'queue' bit must be set.
+ */
+
+#define VGE_TXDESC_Q 0x8000
+
+struct vge_tx_desc {
+ uint32_t vge_sts;
+ uint32_t vge_ctl;
+ struct vge_tx_frag vge_frag[VGE_TX_FRAGS];
+};
+
+#define VGE_TDSTS_COLLCNT 0x0000000F /* TX collision count */
+#define VGE_TDSTS_COLL 0x00000010 /* collision seen */
+#define VGE_TDSTS_OWINCOLL 0x00000020 /* out of window collision */
+#define VGE_TDSTS_OWT 0x00000040 /* jumbo frame tx abort */
+#define VGE_TDSTS_EXCESSCOLL 0x00000080 /* TX aborted, excess colls */
+#define VGE_TDSTS_HBEATFAIL 0x00000100 /* heartbeat detect failed */
+#define VGE_TDSTS_CARRLOSS 0x00000200 /* carrier sense lost */
+#define VGE_TDSTS_SHUTDOWN 0x00000400 /* shutdown during TX */
+#define VGE_TDSTS_LINKFAIL 0x00001000 /* link fail during TX */
+#define VGE_TDSTS_GMII 0x00002000 /* GMII transmission */
+#define VGE_TDSTS_FDX 0x00004000 /* full duplex transmit */
+#define VGE_TDSTS_TXERR 0x00008000 /* error occurred */
+#define VGE_TDSTS_SEGSIZE 0x3FFF0000 /* TCP large send size */
+#define VGE_TDSTS_OWN 0x80000000 /* own bit */
+
+#define VGE_TDCTL_VLANID 0x00000FFF /* VLAN ID */
+#define VGE_TDCTL_CFI 0x00001000 /* VLAN CFI bit */
+#define VGE_TDCTL_PRIO 0x0000E000 /* VLAN prio bits */
+#define VGE_TDCTL_NOCRC 0x00010000 /* disable CRC generation */
+#define VGE_TDCTL_JUMBO 0x00020000 /* jumbo frame */
+#define VGE_TDCTL_TCPCSUM 0x00040000 /* do TCP hw checksum */
+#define VGE_TDCTL_UDPCSUM 0x00080000 /* do UDP hw checksum */
+#define VGE_TDCTL_IPCSUM 0x00100000 /* do IP hw checksum */
+#define VGE_TDCTL_VTAG 0x00200000 /* insert VLAN tag */
+#define VGE_TDCTL_PRIO_INT 0x00400000 /* priority int request */
+#define VGE_TDCTL_TIC 0x00800000 /* transfer int request */
+#define VGE_TDCTL_TCPLSCTL 0x03000000 /* TCP large send ctl */
+#define VGE_TDCTL_FRAGCNT 0xF0000000 /* number of frags used */
+
+#define VGE_TD_LS_MOF 0x00000000 /* middle of large send */
+#define VGE_TD_LS_SOF 0x01000000 /* start of large send */
+#define VGE_TD_LS_EOF 0x02000000 /* end of large send */
+#define VGE_TD_LS_NORM 0x03000000 /* normal frame */
+
+/* Receive DMA descriptors have a single fragment pointer. */
+
+struct vge_rx_desc {
+ volatile uint32_t vge_sts;
+ volatile uint32_t vge_ctl;
+ volatile uint32_t vge_addrlo;
+ volatile uint16_t vge_addrhi;
+ volatile uint16_t vge_buflen;
+};
+
+/*
+ * Like the TX descriptor, the high bit in the buflen field in the
+ * RX descriptor has special meaning. This bit controls whether or
+ * not interrupts are generated for this descriptor.
+ */
+
+#define VGE_RXDESC_I 0x8000
+
+#define VGE_RDSTS_VIDM 0x00000001 /* VLAN tag filter miss */
+#define VGE_RDSTS_CRCERR 0x00000002 /* bad CRC error */
+#define VGE_RDSTS_FAERR 0x00000004 /* frame alignment error */
+#define VGE_RDSTS_CSUMERR 0x00000008 /* bad TCP/IP checksum */
+#define VGE_RDSTS_RLERR 0x00000010 /* RX length error */
+#define VGE_RDSTS_SYMERR 0x00000020 /* PCS symbol error */
+#define VGE_RDSTS_SNTAG 0x00000040 /* RX'ed tagged SNAP pkt */
+#define VGE_RDSTS_DETAG 0x00000080 /* VLAN tag extracted */
+#define VGE_RDSTS_BOUNDARY 0x00000300 /* frame boundary bits */
+#define VGE_RDSTS_VTAG 0x00000400 /* VLAN tag indicator */
+#define VGE_RDSTS_UCAST 0x00000800 /* unicast frame */
+#define VGE_RDSTS_BCAST 0x00001000 /* broadcast frame */
+#define VGE_RDSTS_MCAST 0x00002000 /* multicast frame */
+#define VGE_RDSTS_PFT 0x00004000 /* perfect filter hit */
+#define VGE_RDSTS_RXOK 0x00008000 /* frame is good. */
+#define VGE_RDSTS_BUFSIZ 0x3FFF0000 /* received frame len */
+#define VGE_RDSTS_SHUTDOWN 0x40000000 /* shutdown during RX */
+#define VGE_RDSTS_OWN 0x80000000 /* own bit. */
+
+#define VGE_RXPKT_ONEFRAG 0x00000000 /* only one fragment */
+#define VGE_RXPKT_EOF 0x00000100 /* first frag in frame */
+#define VGE_RXPKT_SOF 0x00000200 /* last frag in frame */
+#define VGE_RXPKT_MOF 0x00000300 /* intermediate frag */
+
+#define VGE_RDCTL_VLANID 0x0000FFFF /* VLAN ID info */
+#define VGE_RDCTL_UDPPKT 0x00010000 /* UDP packet received */
+#define VGE_RDCTL_TCPPKT 0x00020000 /* TCP packet received */
+#define VGE_RDCTL_IPPKT 0x00040000 /* IP packet received */
+#define VGE_RDCTL_UDPZERO 0x00080000 /* pkt with UDP CSUM of 0 */
+#define VGE_RDCTL_FRAG 0x00100000 /* received IP frag */
+#define VGE_RDCTL_PROTOCSUMOK 0x00200000 /* TCP/UDP checksum ok */
+#define VGE_RDCTL_IPCSUMOK 0x00400000 /* IP checksum ok */
+#define VGE_RDCTL_FILTIDX 0x3C000000 /* interesting filter idx */
+
+#endif /* _IF_VGEREG_H_ */
diff --git a/sys/dev/vge/if_vgevar.h b/sys/dev/vge/if_vgevar.h
new file mode 100644
index 0000000..ad298c4
--- /dev/null
+++ b/sys/dev/vge/if_vgevar.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2004
+ * Bill Paul <wpaul@windriver.com>. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#if !defined(__i386__)
+#define VGE_FIXUP_RX
+#endif
+
+#define VGE_JUMBO_MTU 9000
+
+#define VGE_IFQ_MAXLEN 64
+
+#define VGE_TX_DESC_CNT 256
+#define VGE_RX_DESC_CNT 256 /* Must be a multiple of 4!! */
+#define VGE_RING_ALIGN 256
+#define VGE_RX_LIST_SZ (VGE_RX_DESC_CNT * sizeof(struct vge_rx_desc))
+#define VGE_TX_LIST_SZ (VGE_TX_DESC_CNT * sizeof(struct vge_tx_desc))
+#define VGE_TX_DESC_INC(x) (x = (x + 1) % VGE_TX_DESC_CNT)
+#define VGE_RX_DESC_INC(x) (x = (x + 1) % VGE_RX_DESC_CNT)
+#define VGE_ADDR_LO(y) ((u_int64_t) (y) & 0xFFFFFFFF)
+#define VGE_ADDR_HI(y) ((u_int64_t) (y) >> 32)
+#define VGE_BUFLEN(y) ((y) & 0x7FFF)
+#define VGE_OWN(x) (le32toh((x)->vge_sts) & VGE_RDSTS_OWN)
+#define VGE_RXBYTES(x) ((le32toh((x)->vge_sts) & \
+ VGE_RDSTS_BUFSIZ) >> 16)
+#define VGE_MIN_FRAMELEN 60
+
+#ifdef VGE_FIXUP_RX
+#define VGE_ETHER_ALIGN sizeof(uint32_t)
+#else
+#define VGE_ETHER_ALIGN 0
+#endif
+
+struct vge_type {
+ uint16_t vge_vid;
+ uint16_t vge_did;
+ char *vge_name;
+};
+
+struct vge_softc;
+
+struct vge_dmaload_arg {
+ struct vge_softc *sc;
+ int vge_idx;
+ int vge_maxsegs;
+ struct mbuf *vge_m0;
+ u_int32_t vge_flags;
+};
+
+struct vge_list_data {
+ struct mbuf *vge_tx_mbuf[VGE_TX_DESC_CNT];
+ struct mbuf *vge_rx_mbuf[VGE_RX_DESC_CNT];
+ int vge_tx_prodidx;
+ int vge_rx_prodidx;
+ int vge_tx_considx;
+ int vge_tx_free;
+ bus_dmamap_t vge_tx_dmamap[VGE_TX_DESC_CNT];
+ bus_dmamap_t vge_rx_dmamap[VGE_RX_DESC_CNT];
+ bus_dma_tag_t vge_mtag; /* mbuf mapping tag */
+ bus_dma_tag_t vge_rx_list_tag;
+ bus_dmamap_t vge_rx_list_map;
+ struct vge_rx_desc *vge_rx_list;
+ bus_addr_t vge_rx_list_addr;
+ bus_dma_tag_t vge_tx_list_tag;
+ bus_dmamap_t vge_tx_list_map;
+ struct vge_tx_desc *vge_tx_list;
+ bus_addr_t vge_tx_list_addr;
+};
+
+struct vge_softc {
+ struct arpcom arpcom; /* interface info */
+ device_t vge_dev;
+ bus_space_handle_t vge_bhandle; /* bus space handle */
+ bus_space_tag_t vge_btag; /* bus space tag */
+ struct resource *vge_res;
+ struct resource *vge_irq;
+ void *vge_intrhand;
+ device_t vge_miibus;
+ bus_dma_tag_t vge_parent_tag;
+ bus_dma_tag_t vge_tag;
+ u_int8_t vge_unit; /* interface number */
+ u_int8_t vge_type;
+ int vge_if_flags;
+ int vge_rx_consumed;
+ int vge_link;
+ int vge_camidx;
+ struct task vge_txtask;
+ struct mtx vge_mtx;
+ struct mbuf *vge_head;
+ struct mbuf *vge_tail;
+
+ struct vge_list_data vge_ldata;
+
+ int suspended; /* 0 = normal 1 = suspended */
+#ifdef DEVICE_POLLING
+ int rxcycles;
+#endif
+
+ u_int32_t saved_maps[5]; /* pci data */
+ u_int32_t saved_biosaddr;
+ u_int8_t saved_intline;
+ u_int8_t saved_cachelnsz;
+ u_int8_t saved_lattimer;
+};
+
+#define VGE_LOCK(_sc) mtx_lock(&(_sc)->vge_mtx)
+#define VGE_UNLOCK(_sc) mtx_unlock(&(_sc)->vge_mtx)
+#define VGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->vge_mtx, MA_OWNED)
+
+/*
+ * register space access macros
+ */
+#define CSR_WRITE_STREAM_4(sc, reg, val) \
+ bus_space_write_stream_4(sc->vge_btag, sc->vge_bhandle, reg, val)
+#define CSR_WRITE_4(sc, reg, val) \
+ bus_space_write_4(sc->vge_btag, sc->vge_bhandle, reg, val)
+#define CSR_WRITE_2(sc, reg, val) \
+ bus_space_write_2(sc->vge_btag, sc->vge_bhandle, reg, val)
+#define CSR_WRITE_1(sc, reg, val) \
+ bus_space_write_1(sc->vge_btag, sc->vge_bhandle, reg, val)
+
+#define CSR_READ_4(sc, reg) \
+ bus_space_read_4(sc->vge_btag, sc->vge_bhandle, reg)
+#define CSR_READ_2(sc, reg) \
+ bus_space_read_2(sc->vge_btag, sc->vge_bhandle, reg)
+#define CSR_READ_1(sc, reg) \
+ bus_space_read_1(sc->vge_btag, sc->vge_bhandle, reg)
+
+#define CSR_SETBIT_1(sc, reg, x) \
+ CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x))
+#define CSR_SETBIT_2(sc, reg, x) \
+ CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x))
+#define CSR_SETBIT_4(sc, reg, x) \
+ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x))
+
+#define CSR_CLRBIT_1(sc, reg, x) \
+ CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x))
+#define CSR_CLRBIT_2(sc, reg, x) \
+ CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x))
+#define CSR_CLRBIT_4(sc, reg, x) \
+ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x))
+
+#define VGE_TIMEOUT 10000
+
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 37de17f..a82b45b 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -203,6 +203,8 @@ device bfe # Broadcom BCM440x 10/100 Ethernet
device bge # Broadcom BCM570xx Gigabit Ethernet
device dc # DEC/Intel 21143 and various workalikes
device fxp # Intel EtherExpress PRO/100B (82557, 82558)
+device lge # Level 1 LXT1001 gigabit ethernet
+device nge # NatSemi DP83820 gigabit ethernet
device pcn # AMD Am79C97x PCI 10/100 (precedence over 'lnc')
device re # RealTek 8139C+/8169/8169S/8110S
device rl # RealTek 8129/8139
@@ -213,6 +215,7 @@ device ste # Sundance ST201 (D-Link DFE-550TX)
device ti # Alteon Networks Tigon I/II gigabit Ethernet
device tl # Texas Instruments ThunderLAN
device tx # SMC EtherPower II (83c170 ``EPIC'')
+device vge # VIA VT612x gigabit ethernet
device vr # VIA Rhine, Rhine II
device wb # Winbond W89C840F
device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'')
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 207ca28..1aae7c6 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -240,6 +240,7 @@ SUBDIR= ${_3dfx} \
uvisor \
uvscom \
${_vesa} \
+ vge \
vinum \
vpo \
vr \
diff --git a/sys/modules/vge/Makefile b/sys/modules/vge/Makefile
new file mode 100644
index 0000000..8569f68
--- /dev/null
+++ b/sys/modules/vge/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/vge
+
+KMOD= if_vge
+SRCS= if_vge.c miibus_if.h opt_bdg.h device_if.h bus_if.h pci_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC
index 9d69d81..a38c34d 100644
--- a/sys/pc98/conf/GENERIC
+++ b/sys/pc98/conf/GENERIC
@@ -186,6 +186,8 @@ device bfe # Broadcom BCM440x 10/100 Ethernet
device bge # Broadcom BCM570xx Gigabit Ethernet
device dc # DEC/Intel 21143 and various workalikes
device fxp # Intel EtherExpress PRO/100B (82557, 82558)
+device lge # Level 1 LXT1001 gigabit ethernet
+device nge # NatSemi DP83820 gigabit ethernet
device pcn # AMD Am79C97x PCI 10/100 (precedence over 'lnc')
device re # RealTek 8139C+/8169/8169S/8110S
device rl # RealTek 8129/8139
@@ -196,6 +198,7 @@ device ste # Sundance ST201 (D-Link DFE-550TX)
device ti # Alteon Networks Tigon I/II gigabit Ethernet
device tl # Texas Instruments ThunderLAN
device tx # SMC EtherPower II (83c170 ``EPIC'')
+device vge # VIA VT612x gigabit ethernet
device vr # VIA Rhine, Rhine II
device wb # Winbond W89C840F
device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'')
OpenPOWER on IntegriCloud