Date: Sun, 11 Nov 2007 19:37:30 GMT From: Oleksandr Tymoshenko <gonzo@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 128941 for review Message-ID: <200711111937.lABJbURI068536@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=128941 Change 128941 by gonzo@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) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200711111937.lABJbURI068536>
