PERFORCE change 128941 for review
Oleksandr Tymoshenko
gonzo at FreeBSD.org
Sun Nov 11 11:37:31 PST 2007
http://perforce.freebsd.org/chv.cgi?CH=128941
Change 128941 by gonzo at gonzo_jeeves on 2007/11/11 19:37:03
o Merge yongari's version of if_vr with newbus stuff.
o During reset wait while reset bit is cleared instead of
DELAY(200);
Affected files ...
.. //depot/projects/mips2/src/sys/pci/if_vr.c#7 edit
.. //depot/projects/mips2/src/sys/pci/if_vrreg.h#6 edit
Differences ...
==== //depot/projects/mips2/src/sys/pci/if_vr.c#7 (text+ko) ====
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/pci/if_vr.c,v 1.127 2007/10/12 03:32:55 yongari Exp $");
+__FBSDID("$FreeBSD: src/sys/pci/if_vr.c,v 1.126 2007/04/23 12:19:02 phk Exp $");
/*
* VIA Rhine fast ethernet PCI NIC driver
@@ -65,6 +65,7 @@
#endif
#include <sys/param.h>
+#include <sys/endian.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
@@ -72,22 +73,24 @@
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
+#include <net/if_vlan_var.h>
#include <net/bpf.h>
-#include <vm/vm.h> /* for vtophys */
-#include <vm/pmap.h> /* for vtophys */
#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>
@@ -95,7 +98,11 @@
#define VR_USEIOSPACE
+#if 0
#include <pci/if_vrreg.h>
+#else
+#include "if_vrreg.h"
+#endif
MODULE_DEPEND(vr, pci, 1, 1, 1);
MODULE_DEPEND(vr, ether, 1, 1, 1);
@@ -104,12 +111,18 @@
/* "device miibus" required. See GENERIC if you get errors here. */
#include "miibus_if.h"
+/* Show Rx/Tx error status */
+#define VR_SHOW_ERRORS
+
+#define VR_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
+
/*
* Various supported device vendors/types, their names & quirks
*/
#define VR_Q_NEEDALIGN (1<<0)
#define VR_Q_CSUM (1<<1)
+#define VR_Q_CAM (1<<2)
static struct vr_type {
u_int16_t vr_vid;
@@ -130,7 +143,7 @@
0,
"VIA VT6105 Rhine III 10/100BaseTX" },
{ VIA_VENDORID, VIA_DEVICEID_RHINE_III_M,
- VR_Q_CSUM,
+ VR_Q_CSUM | VR_Q_CAM,
"VIA VT6105M Rhine III 10/100BaseTX" },
{ DELTA_VENDORID, DELTA_DEVICEID_RHINE_II,
VR_Q_NEEDALIGN,
@@ -141,81 +154,86 @@
{ 0, 0, 0, NULL }
};
-struct vr_list_data {
- struct vr_desc vr_rx_list[VR_RX_LIST_CNT];
- struct vr_desc vr_tx_list[VR_TX_LIST_CNT];
-};
-
-struct vr_softc {
- struct ifnet *vr_ifp; /* interface info */
- device_t vr_dev;
- struct resource *vr_res;
- struct resource *vr_irq;
- void *vr_intrhand;
- device_t vr_miibus;
- u_int8_t vr_revid; /* Rhine chip revision */
- u_int8_t vr_flags; /* See VR_F_* below */
- struct vr_list_data *vr_ldata;
- struct callout vr_stat_callout;
- struct mtx vr_mtx;
- int vr_suspended; /* if 1, sleeping/detaching */
- int vr_quirks;
- struct vr_desc *vr_rx_head;
- struct vr_desc *vr_tx_cons;
- struct vr_desc *vr_tx_prod;
-#ifdef DEVICE_POLLING
- int rxcycles;
-#endif
-};
-
static int vr_probe(device_t);
static int vr_attach(device_t);
static int vr_detach(device_t);
+static void vr_shutdown(device_t);
+static int vr_suspend(device_t);
+static int vr_resume(device_t);
-static int vr_newbuf(struct vr_desc *, struct mbuf *);
+static void vr_dmamap_cb(void *, bus_dma_segment_t *, int, int);
+static int vr_dma_alloc(struct vr_softc *);
+static void vr_dma_free(struct vr_softc *);
+static __inline void vr_discard_rxbuf(struct vr_rxdesc *);
+static int vr_newbuf(struct vr_softc *, int);
+#ifndef __NO_STRICT_ALIGNMENT
+static __inline void vr_fixup_rx(struct mbuf *);
+#endif
static void vr_rxeof(struct vr_softc *);
-static void vr_rxeoc(struct vr_softc *);
static void vr_txeof(struct vr_softc *);
static void vr_tick(void *);
+static int vr_error(struct vr_softc *, uint16_t);
+static void vr_tx_underrun(struct vr_softc *);
static void vr_intr(void *);
static void vr_start(struct ifnet *);
static void vr_start_locked(struct ifnet *);
+static struct mbuf *vr_defrag(struct mbuf *, int, int);
+static int vr_encap(struct vr_softc *, struct mbuf **);
static int vr_ioctl(struct ifnet *, u_long, caddr_t);
static void vr_init(void *);
static void vr_init_locked(struct vr_softc *);
+static void vr_vlan_setup(struct vr_softc *);
+static void vr_tx_start(struct vr_softc *);
+static void vr_rx_start(struct vr_softc *);
+static int vr_tx_stop(struct vr_softc *);
+static int vr_rx_stop(struct vr_softc *);
static void vr_stop(struct vr_softc *);
-static void vr_watchdog(struct ifnet *);
-static void vr_shutdown(device_t);
+static void vr_watchdog(struct vr_softc *);
static int vr_ifmedia_upd(struct ifnet *);
static void vr_ifmedia_sts(struct ifnet *, struct ifmediareq *);
-static int vr_mii_readreg(const struct vr_softc *, struct vr_mii_frame *);
-static int vr_mii_writereg(const struct vr_softc *, const struct vr_mii_frame *);
-static int vr_miibus_readreg(device_t, uint16_t, uint16_t);
-static int vr_miibus_writereg(device_t, uint16_t, uint16_t, uint16_t);
+static int vr_miibus_readreg(device_t, int, int);
+static int vr_miibus_writereg(device_t, int, int, int);
static void vr_miibus_statchg(device_t);
-static void vr_setcfg(struct vr_softc *, int);
-static void vr_setmulti(struct vr_softc *);
+static void vr_link_task(void *, int);
+static int vr_setperf(struct vr_softc *, int, uint8_t *);
+static void vr_set_filter(struct vr_softc *);
static void vr_reset(const struct vr_softc *);
-static int vr_list_rx_init(struct vr_softc *);
-static int vr_list_tx_init(struct vr_softc *);
+static int vr_tx_ring_init(struct vr_softc *);
+static int vr_rx_ring_init(struct vr_softc *);
+static int vr_sysctl_stats(SYSCTL_HANDLER_ARGS);
#ifdef VR_USEIOSPACE
#define VR_RES SYS_RES_IOPORT
-#define VR_RID VR_PCI_LOIO
+#define VR_RID PCIR_BAR(0)
#else
#define VR_RES SYS_RES_MEMORY
-#define VR_RID VR_PCI_LOMEM
+#define VR_RID PCIR_BAR(1)
#endif
+static struct vr_tx_threshold_table {
+ int tx_cfg;
+ int bcr_cfg;
+ int value;
+} vr_tx_threshold_tables[] = {
+ { VR_TXTHRESH_64BYTES, VR_BCR1_TXTHRESH64BYTES, 64 },
+ { VR_TXTHRESH_128BYTES, VR_BCR1_TXTHRESH128BYTES, 128 },
+ { VR_TXTHRESH_256BYTES, VR_BCR1_TXTHRESH256BYTES, 256 },
+ { VR_TXTHRESH_512BYTES, VR_BCR1_TXTHRESH512BYTES, 512 },
+ { VR_TXTHRESH_1024BYTES, VR_BCR1_TXTHRESH1024BYTES, 1024 },
+ { VR_TXTHRESH_STORENFWD, VR_BCR1_TXTHRESHSTORENFWD, 2048 }
+};
+
static device_method_t vr_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, vr_probe),
DEVMETHOD(device_attach, vr_attach),
DEVMETHOD(device_detach, vr_detach),
DEVMETHOD(device_shutdown, vr_shutdown),
+ DEVMETHOD(device_suspend, vr_suspend),
+ DEVMETHOD(device_resume, vr_resume),
/* bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
@@ -225,8 +243,9 @@
DEVMETHOD(miibus_readreg, vr_miibus_readreg),
DEVMETHOD(miibus_writereg, vr_miibus_writereg),
DEVMETHOD(miibus_statchg, vr_miibus_statchg),
+ DEVMETHOD(miibus_linkchg, vr_miibus_statchg),
- { 0, 0 }
+ { NULL, NULL }
};
static driver_t vr_driver = {
@@ -239,132 +258,167 @@
DRIVER_MODULE(vr, pci, vr_driver, vr_devclass, 0, 0);
DRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0);
-#define VR_F_RESTART 0x01 /* Restart unit on next tick */
-
-#define VR_LOCK(_sc) mtx_lock(&(_sc)->vr_mtx)
-#define VR_UNLOCK(_sc) mtx_unlock(&(_sc)->vr_mtx)
-#define VR_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->vr_mtx, MA_OWNED)
-
-/*
- * register space access macros
- */
-#define CSR_WRITE_4(sc, reg, val) bus_write_4(sc->vr_res, reg, val)
-#define CSR_WRITE_2(sc, reg, val) bus_write_2(sc->vr_res, reg, val)
-#define CSR_WRITE_1(sc, reg, val) bus_write_1(sc->vr_res, reg, val)
-
-#define CSR_READ_2(sc, reg) bus_read_2(sc->vr_res, reg)
-#define CSR_READ_1(sc, reg) bus_read_1(sc->vr_res, reg)
-#define VR_SETBIT(sc, reg, x) CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x))
-#define VR_CLRBIT(sc, reg, x) CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x))
-
-#define VR_SETBIT16(sc, reg, x) CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x))
-#define VR_CLRBIT16(sc, reg, x) CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x))
-
-
-/*
- * Read an PHY register through the MII.
- */
static int
-vr_mii_readreg(const struct vr_softc *sc, struct vr_mii_frame *frame)
+vr_miibus_readreg(device_t dev, int phy, int reg)
{
- int i;
+ struct vr_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ if (sc->vr_phyaddr != phy)
+ return (0);
/* Set the PHY address. */
- CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)|
- frame->mii_phyaddr);
-
+ CSR_WRITE_1(sc, VR_PHYADDR, phy);
/* Set the register address. */
- CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr);
+ CSR_WRITE_1(sc, VR_MIIADDR, reg);
VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_READ_ENB);
- for (i = 0; i < 10000; i++) {
+ for (i = 0; i < VR_MII_TIMEOUT; i++) {
+ DELAY(1);
if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_READ_ENB) == 0)
break;
- DELAY(1);
}
- frame->mii_data = CSR_READ_2(sc, VR_MIIDATA);
+ if (i == VR_MII_TIMEOUT)
+ device_printf(sc->vr_dev, "phy read fail %d:%d\n", phy, reg);
- return (0);
+ return (CSR_READ_2(sc, VR_MIIDATA));
}
-
-/*
- * Write to a PHY register through the MII.
- */
static int
-vr_mii_writereg(const struct vr_softc *sc, const struct vr_mii_frame *frame)
+vr_miibus_writereg(device_t dev, int phy, int reg, int data)
{
- int i;
+ struct vr_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ if (sc->vr_phyaddr != phy)
+ return (0);
/* Set the PHY address. */
- CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)|
- frame->mii_phyaddr);
-
+ CSR_WRITE_1(sc, VR_PHYADDR, phy);
/* Set the register address and data to write. */
- CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr);
- CSR_WRITE_2(sc, VR_MIIDATA, frame->mii_data);
-
+ CSR_WRITE_1(sc, VR_MIIADDR, reg);
+ CSR_WRITE_2(sc, VR_MIIDATA, data);
VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_WRITE_ENB);
- for (i = 0; i < 10000; i++) {
+ for (i = 0; i < VR_MII_TIMEOUT; i++) {
+ DELAY(1);
if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_WRITE_ENB) == 0)
break;
- DELAY(1);
}
+ if (i == VR_MII_TIMEOUT)
+ device_printf(sc->vr_dev, "phy write fail %d:%d\n", phy, reg);
return (0);
}
-static int
-vr_miibus_readreg(device_t dev, uint16_t phy, uint16_t reg)
+static void
+vr_miibus_statchg(device_t dev)
{
- struct vr_mii_frame frame;
- struct vr_softc *sc = device_get_softc(dev);
+ struct vr_softc *sc;
- if (sc->vr_revid == REV_ID_VT6102_APOLLO && phy != 1)
- return (0);
-
- bzero((char *)&frame, sizeof(frame));
- frame.mii_phyaddr = phy;
- frame.mii_regaddr = reg;
- vr_mii_readreg(sc, &frame);
- return (frame.mii_data);
+ sc = device_get_softc(dev);
+ taskqueue_enqueue(taskqueue_swi, &sc->vr_link_task);
}
-static int
-vr_miibus_writereg(device_t dev, uint16_t phy, uint16_t reg, uint16_t data)
+/*
+ * In order to fiddle with the
+ * 'full-duplex' and '100Mbps' bits in the netconfig register, we
+ * first have to put the transmit and/or receive logic in the idle state.
+ */
+static void
+vr_link_task(void *arg, int pending)
{
- struct vr_mii_frame frame;
- struct vr_softc *sc = device_get_softc(dev);
+ struct vr_softc *sc;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+ int lfdx, mfdx;
+ uint8_t cr0, cr1;
+
+ sc = (struct vr_softc *)arg;
- if (sc->vr_revid == REV_ID_VT6102_APOLLO && phy != 1)
- return (0);
+ VR_LOCK(sc);
+ mii = device_get_softc(sc->vr_miibus);
+ ifp = sc->vr_ifp;
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ VR_UNLOCK(sc);
+ return;
+ }
- bzero((char *)&frame, sizeof(frame));
- frame.mii_phyaddr = phy;
- frame.mii_regaddr = reg;
- frame.mii_data = data;
- vr_mii_writereg(sc, &frame);
+ if (mii->mii_media_status & IFM_ACTIVE) {
+ if (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
+ sc->vr_link = 1;
+ } else
+ sc->vr_link = 0;
- return (0);
+ if (sc->vr_link) {
+ /* XXX flow control */
+ cr0 = CSR_READ_1(sc, VR_CR0);
+ cr1 = CSR_READ_1(sc, VR_CR1);
+ mfdx = (cr1 & VR_CR1_FULLDUPLEX) != 0;
+ lfdx = (IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0;
+ if (mfdx != lfdx) {
+ if ((cr0 & (VR_CR0_TX_ON | VR_CR0_RX_ON)) != 0) {
+ if (vr_tx_stop(sc) < 0 || vr_rx_stop(sc) < 0) {
+ printf("T1\n");
+ sc->vr_flags |= VR_F_RESTART;
+ VR_UNLOCK(sc);
+ return;
+ }
+ }
+ if (lfdx)
+ cr1 |= VR_CR1_FULLDUPLEX;
+ else
+ cr1 &= ~VR_CR1_FULLDUPLEX;
+ CSR_WRITE_1(sc, VR_CR1, cr1);
+ }
+ vr_rx_start(sc);
+ vr_tx_start(sc);
+ } else {
+ if (vr_tx_stop(sc) < 0 || vr_rx_stop(sc) < 0) {
+ printf("T2\n");
+ sc->vr_flags |= VR_F_RESTART;
+ VR_UNLOCK(sc);
+ return;
+ }
+ }
+ VR_UNLOCK(sc);
}
-static void
-vr_miibus_statchg(device_t dev)
+/*
+ * Copy the address 'mac' into the perfect RX filter entry at
+ * offset 'idx.' The perfect filter only has 32 entries so do
+ * some sanity tests.
+ */
+static int
+vr_setperf(struct vr_softc *sc, int idx, uint8_t *mac)
{
- struct mii_data *mii;
- struct vr_softc *sc = device_get_softc(dev);
+ int i;
+
+ if (idx < 0 || idx >= VR_CAM_MCAST_CNT || mac == NULL)
+ return (EINVAL);
+
+ /* Set CAM entry address. */
+ CSR_WRITE_1(sc, VR_CAMADDR, idx);
+ /* Set CAM entry data. */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ CSR_WRITE_1(sc, VR_MAR0 + i, mac[i]);
+ DELAY(10);
+ /* Set CAM write. */
+ CSR_WRITE_1(sc, VR_CAMCTL, VR_CAMCTL_ENA | VR_CAMCTL_WRITE);
+ DELAY(10);
- mii = device_get_softc(sc->vr_miibus);
- vr_setcfg(sc, mii->mii_media_active);
+ return(0);
}
/*
* Program the 64-bit multicast hash filter.
*/
static void
-vr_setmulti(struct vr_softc *sc)
+vr_set_filter(struct vr_softc *sc)
{
struct ifnet *ifp = sc->vr_ifp;
int h = 0;
@@ -372,13 +426,19 @@
struct ifmultiaddr *ifma;
uint8_t rxfilt;
int mcnt = 0;
+ uint32_t cam_mask;
VR_LOCK_ASSERT(sc);
rxfilt = CSR_READ_1(sc, VR_RXCFG);
+ rxfilt = ~(VR_RXCFG_RX_PROMISC | VR_RXCFG_RX_BROAD | VR_RXCFG_RX_MULTI);
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxfilt |= VR_RXCFG_RX_BROAD;
if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
rxfilt |= VR_RXCFG_RX_MULTI;
+ if (ifp->if_flags & IFF_PROMISC)
+ rxfilt |= VR_RXCFG_RX_PROMISC;
CSR_WRITE_1(sc, VR_RXCFG, rxfilt);
CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF);
CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF);
@@ -389,11 +449,26 @@
CSR_WRITE_4(sc, VR_MAR0, 0);
CSR_WRITE_4(sc, VR_MAR1, 0);
+ if (sc->vr_quirks & VR_Q_CAM)
+ CSR_WRITE_1(sc, VR_CAMCTL, VR_CAMCTL_ENA | VR_CAMCTL_MCAST);
+ cam_mask = 0;
/* Now program new ones. */
IF_ADDR_LOCK(ifp);
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
+ /*
+ * Program the first VR_CAM_MCAST_CNT multicast
+ * groups into the perfect filter. For all others,
+ * use the hash table.
+ */
+ if (sc->vr_quirks & VR_Q_CAM && mcnt < VR_CAM_MCAST_CNT) {
+ vr_setperf(sc, mcnt,
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ cam_mask |= 1 << mcnt;
+ mcnt++;
+ continue;
+ }
h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
if (h < 32)
@@ -404,68 +479,57 @@
}
IF_ADDR_UNLOCK(ifp);
+ if (sc->vr_quirks & VR_Q_CAM) {
+ /* Set CAM mask. */
+ CSR_WRITE_1(sc, VR_CAMMASK, cam_mask);
+ CSR_WRITE_1(sc, VR_CAMCTL, 0);
+ /* Disable VLAN CAM. */
+ CSR_WRITE_1(sc, VR_CAMCTL, VR_CAMCTL_ENA | VR_CAMCTL_VLAN);
+ CSR_WRITE_1(sc, VR_CAMMASK, 0);
+ CSR_WRITE_1(sc, VR_CAMCTL, 0);
+ }
+
if (mcnt)
rxfilt |= VR_RXCFG_RX_MULTI;
- else
- rxfilt &= ~VR_RXCFG_RX_MULTI;
CSR_WRITE_4(sc, VR_MAR0, hashes[0]);
CSR_WRITE_4(sc, VR_MAR1, hashes[1]);
CSR_WRITE_1(sc, VR_RXCFG, rxfilt);
}
-/*
- * In order to fiddle with the
- * 'full-duplex' and '100Mbps' bits in the netconfig register, we
- * first have to put the transmit and/or receive logic in the idle state.
- */
-static void
-vr_setcfg(struct vr_softc *sc, int media)
-{
- int restart = 0;
-
- VR_LOCK_ASSERT(sc);
-
- if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) {
- restart = 1;
- VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON));
- }
-
- if ((media & IFM_GMASK) == IFM_FDX)
- VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX);
- else
- VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX);
-
- if (restart)
- VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON);
-}
-
static void
vr_reset(const struct vr_softc *sc)
{
- register int i;
+ int i;
/*VR_LOCK_ASSERT(sc);*/ /* XXX: Called during attach w/o lock. */
- VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET);
-
+ CSR_WRITE_1(sc, VR_CR1, VR_CR1_RESET);
+ if (sc->vr_revid < REV_ID_VT6102_A) {
+ /* VT86C100A needs more delay after reset. */
+ DELAY(100);
+ }
for (i = 0; i < VR_TIMEOUT; i++) {
DELAY(10);
- if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET))
+ if (!(CSR_READ_1(sc, VR_CR1) & VR_CR1_RESET))
break;
}
if (i == VR_TIMEOUT) {
- if (sc->vr_revid < REV_ID_VT3065_A)
+ if (sc->vr_revid < REV_ID_VT6102_A)
device_printf(sc->vr_dev, "reset never completed!\n");
else {
/* Use newer force reset command */
- device_printf(sc->vr_dev, "Using force reset command.\n");
+ device_printf(sc->vr_dev,
+ "Using force reset command.\n");
VR_SETBIT(sc, VR_MISC_CR1, VR_MISCCR1_FORSRST);
+ /*
+ * Wait a little while for the chip to get its brains
+ * in order.
+ */
+ DELAY(2000);
}
}
- /* Wait a little while for the chip to get its brains in order. */
- DELAY(1000);
}
/*
@@ -526,12 +590,18 @@
mtx_init(&sc->vr_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
MTX_DEF);
callout_init_mtx(&sc->vr_stat_callout, &sc->vr_mtx, 0);
+ TASK_INIT(&sc->vr_link_task, 0, vr_link_task, sc);
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+ vr_sysctl_stats, "I", "Statistics");
/*
* Map control/status registers.
*/
pci_enable_busmaster(dev);
- sc->vr_revid = pci_read_config(dev, VR_PCI_REVID, 4) & 0x000000FF;
+ sc->vr_revid = pci_get_revid(dev);
+ device_printf(dev, "Revision: 0x%x\n", sc->vr_revid);
rid = VR_RID;
sc->vr_res = bus_alloc_resource_any(dev, VR_RES, &rid, RF_ACTIVE);
@@ -556,34 +626,40 @@
/* Allocate ifnet structure. */
ifp = sc->vr_ifp = if_alloc(IFT_ETHER);
if (ifp == NULL) {
- device_printf(dev, "can not if_alloc()\n");
+ device_printf(dev, "couldn't allocate ifnet structure\n");
error = ENOSPC;
goto fail;
}
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 = vr_ioctl;
ifp->if_start = vr_start;
- ifp->if_watchdog = vr_watchdog;
ifp->if_init = vr_init;
- IFQ_SET_MAXLEN(&ifp->if_snd, VR_TX_LIST_CNT - 1);
- ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1;
+ IFQ_SET_MAXLEN(&ifp->if_snd, VR_TX_RING_CNT - 1);
+ ifp->if_snd.ifq_maxlen = VR_TX_RING_CNT - 1;
IFQ_SET_READY(&ifp->if_snd);
+ /* Configure Tx FIFO threshold */
+ sc->vr_txthresh = VR_TXTHRESH_MIN;
if (sc->vr_quirks & VR_Q_CSUM) {
- ifp->if_hwassist = (CSUM_IP | CSUM_TCP | CSUM_UDP);
+ ifp->if_hwassist = VR_CSUM_FEATURES;
ifp->if_capabilities |= IFCAP_HWCSUM;
+ /*
+ * To update checksum field the hardware may need to
+ * store entire frames into FIFO before transmitting.
+ */
+ sc->vr_txthresh = VR_TXTHRESH_MAX;
}
ifp->if_capabilities |= IFCAP_VLAN_MTU;
+ if (sc->vr_revid >= REV_ID_VT6105M_A0) {
+ /* 6105M supports HW VLAN tag insertion/extraction. */
+ ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
+ if (sc->vr_quirks & VR_Q_CSUM)
+ ifp->if_capabilities |= IFCAP_VLAN_HWCSUM;
+ }
ifp->if_capenable = ifp->if_capabilities;
- if (ifp->if_capenable & IFCAP_TXCSUM)
- ifp->if_hwassist = (CSUM_IP | CSUM_TCP | CSUM_UDP);
- else
- ifp->if_hwassist = 0;
-
#ifdef DEVICE_POLLING
ifp->if_capabilities |= IFCAP_POLLING;
#endif
@@ -598,7 +674,13 @@
/* Reset the adapter. */
vr_reset(sc);
+ /* Ack intr & disable further interrupts. */
+ CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
+ CSR_WRITE_2(sc, VR_IMR, 0);
+ if (sc->vr_revid >= REV_ID_VT6102_A)
+ CSR_WRITE_2(sc, VR_MII_IMR, 0);
+#if 0
/*
* Turn on bit2 (MIION) in PCI configuration register 0x53 during
* initialization and disable AUTOPOLL.
@@ -606,6 +688,35 @@
pci_write_config(dev, VR_PCI_MODE,
pci_read_config(dev, VR_PCI_MODE, 4) | (VR_MODE3_MIION << 24), 4);
VR_CLRBIT(sc, VR_MIICMD, VR_MIICMD_AUTOPOLL);
+#else
+ if (sc->vr_revid < REV_ID_VT6102_A) {
+ pci_write_config(dev, VR_PCI_MODE2,
+ pci_read_config(dev, VR_PCI_MODE2, 1) |
+ VR_MODE2_MODE10T, 1);
+ } else {
+ /* Report error instead of retrying forever. */
+ pci_write_config(dev, VR_PCI_MODE2,
+ pci_read_config(dev, VR_PCI_MODE2, 1) |
+ VR_MODE2_PCEROPT, 1);
+ /* Detect MII coding error. */
+ pci_write_config(dev, VR_PCI_MODE3,
+ pci_read_config(dev, VR_PCI_MODE3, 1) |
+ VR_MODE3_MIION, 1);
+ if (sc->vr_revid >= REV_ID_VT6105_LOM &&
+ sc->vr_revid < REV_ID_VT6105M_A0)
+ pci_write_config(dev, VR_PCI_MODE2,
+ pci_read_config(dev, VR_PCI_MODE2, 1) |
+ VR_MODE2_MODE10T, 1);
+ /* Enable Memory-Read-Multiple. */
+ if (sc->vr_revid >= REV_ID_VT6107_A1 &&
+ sc->vr_revid < REV_ID_VT6105M_A0)
+ pci_write_config(dev, VR_PCI_MODE2,
+ pci_read_config(dev, VR_PCI_MODE2, 1) |
+ VR_MODE2_MRDPL, 1);
+ }
+ /* Disable MII AUTOPOLL. */
+ VR_CLRBIT(sc, VR_MIICMD, VR_MIICMD_AUTOPOLL);
+#endif
/*
* Get station address. The way the Rhine chips work,
@@ -615,19 +726,23 @@
* registers.
*/
VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD);
- DELAY(200);
+ while ((CSR_READ_4(sc, VR_EECSR) & VR_EECSR_LOAD)) {
+ DELAY(10);
+ }
+
for (i = 0; i < ETHER_ADDR_LEN; i++)
eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i);
- sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF,
- M_NOWAIT | M_ZERO, 0, 0xffffffff, PAGE_SIZE, 0);
-
- if (sc->vr_ldata == NULL) {
- device_printf(dev, "no memory for list buffers!\n");
+ if (vr_dma_alloc(sc) != 0) {
error = ENXIO;
goto fail;
}
+ /* Save PHY address. */
+ if (sc->vr_revid >= REV_ID_VT6105_A0)
+ sc->vr_phyaddr = 1;
+ else
+ sc->vr_phyaddr = CSR_READ_1(sc, VR_PHYADDR) & VR_PHYADDR_MASK;
/* Do MII setup. */
if (mii_phy_probe(dev, &sc->vr_miibus,
vr_ifmedia_upd, vr_ifmedia_sts)) {
@@ -638,8 +753,12 @@
/* Call MI attach routine. */
ether_ifattach(ifp, eaddr);
-
- sc->vr_suspended = 0;
+ /*
+ * Tell the upper layer(s) we support long frames.
+ * Must appear after the call to ether_ifattach() because
+ * ether_ifattach() sets ifi_hdrlen to the default value.
+ */
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
/* Hook interrupt last to avoid having to lock softc */
error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET | INTR_MPSAFE,
@@ -674,7 +793,7 @@
KASSERT(mtx_initialized(&sc->vr_mtx), ("vr mutex not initialized"));
#ifdef DEVICE_POLLING
- if (ifp->if_capenable & IFCAP_POLLING)
+ if (ifp != NULL && ifp->if_capenable & IFCAP_POLLING)
ether_poll_deregister(ifp);
#endif
@@ -682,9 +801,11 @@
if (device_is_attached(dev)) {
VR_LOCK(sc);
sc->vr_suspended = 1;
+ sc->vr_detach = 1;
vr_stop(sc);
VR_UNLOCK(sc);
callout_drain(&sc->vr_stat_callout);
+ taskqueue_drain(taskqueue_swi, &sc->vr_link_task);
ether_ifdetach(ifp);
}
if (sc->vr_miibus)
@@ -701,78 +822,366 @@
if (ifp)
if_free(ifp);
- if (sc->vr_ldata)
- contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF);
+ vr_dma_free(sc);
mtx_destroy(&sc->vr_mtx);
return (0);
}
+struct vr_dmamap_arg {
+ bus_addr_t vr_busaddr;
+};
+
+static void
+vr_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct vr_dmamap_arg *ctx;
+
+ if (error != 0)
+ return;
+ ctx = arg;
+ ctx->vr_busaddr = segs[0].ds_addr;
+}
+
+static int
+vr_dma_alloc(struct vr_softc *sc)
+{
+ struct vr_dmamap_arg ctx;
+ struct vr_txdesc *txd;
+ struct vr_rxdesc *rxd;
+ bus_size_t alignment;
+ int error, i;
+
+ /* Create parent DMA tag. */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->vr_dev), /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */
+ 0, /* nsegments */
+ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->vr_cdata.vr_parent_tag);
+ if (error != 0) {
+ device_printf(sc->vr_dev, "failed to create parent DMA tag\n");
+ goto fail;
+ }
+ /* Create tag for Tx ring. */
+ error = bus_dma_tag_create(
+ sc->vr_cdata.vr_parent_tag, /* parent */
+ VR_RING_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ VR_TX_RING_SIZE, /* maxsize */
+ 1, /* nsegments */
+ VR_TX_RING_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->vr_cdata.vr_tx_ring_tag);
+ if (error != 0) {
+ device_printf(sc->vr_dev, "failed to create Tx ring DMA tag\n");
+ goto fail;
+ }
+
+ /* Create tag for Rx ring. */
+ error = bus_dma_tag_create(
+ sc->vr_cdata.vr_parent_tag, /* parent */
+ VR_RING_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ VR_RX_RING_SIZE, /* maxsize */
+ 1, /* nsegments */
+ VR_RX_RING_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->vr_cdata.vr_rx_ring_tag);
+ if (error != 0) {
+ device_printf(sc->vr_dev, "failed to create Rx ring DMA tag\n");
+ goto fail;
+ }
+
+ if ((sc->vr_quirks & VR_Q_NEEDALIGN) != 0)
+ alignment = sizeof(uint32_t);
+ else
+ alignment = 1;
+ /* Create tag for Tx buffers. */
+ error = bus_dma_tag_create(
+ sc->vr_cdata.vr_parent_tag, /* parent */
+ alignment, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES * VR_MAXFRAGS, /* maxsize */
+ VR_MAXFRAGS, /* nsegments */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->vr_cdata.vr_tx_tag);
+ if (error != 0) {
+ device_printf(sc->vr_dev, "failed to create Tx DMA tag\n");
+ goto fail;
+ }
+
+ /* Create tag for Rx buffers. */
+ error = bus_dma_tag_create(
+ sc->vr_cdata.vr_parent_tag, /* parent */
+ VR_RX_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES, /* maxsize */
+ 1, /* nsegments */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->vr_cdata.vr_rx_tag);
+ if (error != 0) {
+ device_printf(sc->vr_dev, "failed to create Rx DMA tag\n");
+ goto fail;
+ }
+
+ /* Allocate DMA'able memory and load the DMA map for Tx ring. */
+ error = bus_dmamem_alloc(sc->vr_cdata.vr_tx_ring_tag,
+ (void **)&sc->vr_rdata.vr_tx_ring, BUS_DMA_WAITOK |
+ BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->vr_cdata.vr_tx_ring_map);
+ if (error != 0) {
+ device_printf(sc->vr_dev,
+ "failed to allocate DMA'able memory for Tx ring\n");
+ goto fail;
+ }
+
+ ctx.vr_busaddr = 0;
+ error = bus_dmamap_load(sc->vr_cdata.vr_tx_ring_tag,
+ sc->vr_cdata.vr_tx_ring_map, sc->vr_rdata.vr_tx_ring,
+ VR_TX_RING_SIZE, vr_dmamap_cb, &ctx, 0);
+ if (error != 0 || ctx.vr_busaddr == 0) {
+ device_printf(sc->vr_dev,
+ "failed to load DMA'able memory for Tx ring\n");
+ goto fail;
+ }
+ sc->vr_rdata.vr_tx_ring_paddr = ctx.vr_busaddr;
+
+ /* Allocate DMA'able memory and load the DMA map for Rx ring. */
+ error = bus_dmamem_alloc(sc->vr_cdata.vr_rx_ring_tag,
+ (void **)&sc->vr_rdata.vr_rx_ring, BUS_DMA_WAITOK |
+ BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->vr_cdata.vr_rx_ring_map);
+ if (error != 0) {
+ device_printf(sc->vr_dev,
+ "failed to allocate DMA'able memory for Rx ring\n");
+ goto fail;
+ }
+
+ ctx.vr_busaddr = 0;
+ error = bus_dmamap_load(sc->vr_cdata.vr_rx_ring_tag,
+ sc->vr_cdata.vr_rx_ring_map, sc->vr_rdata.vr_rx_ring,
+ VR_RX_RING_SIZE, vr_dmamap_cb, &ctx, 0);
+ if (error != 0 || ctx.vr_busaddr == 0) {
+ device_printf(sc->vr_dev,
+ "failed to load DMA'able memory for Rx ring\n");
+ goto fail;
+ }
+ sc->vr_rdata.vr_rx_ring_paddr = ctx.vr_busaddr;
+
+ /* Create DMA maps for Tx buffers. */
+ for (i = 0; i < VR_TX_RING_CNT; i++) {
+ txd = &sc->vr_cdata.vr_txdesc[i];
+ txd->tx_m = NULL;
+ txd->tx_dmamap = NULL;
+ error = bus_dmamap_create(sc->vr_cdata.vr_tx_tag, 0,
+ &txd->tx_dmamap);
+ if (error != 0) {
+ device_printf(sc->vr_dev,
+ "failed to create Tx dmamap\n");
+ goto fail;
+ }
+ }
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list