diff --git a/src/add-ons/kernel/drivers/network/Jamfile b/src/add-ons/kernel/drivers/network/Jamfile index 5bd657ea13..88f48f92e8 100644 --- a/src/add-ons/kernel/drivers/network/Jamfile +++ b/src/add-ons/kernel/drivers/network/Jamfile @@ -14,7 +14,6 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network wb840 ; # FreeBSD 7 drivers SubInclude HAIKU_TOP src add-ons kernel drivers network broadcom440x ; SubInclude HAIKU_TOP src add-ons kernel drivers network broadcom570x ; -SubInclude HAIKU_TOP src add-ons kernel drivers network jmicron2x0 ; SubInclude HAIKU_TOP src add-ons kernel drivers network marvell_yukon ; SubInclude HAIKU_TOP src add-ons kernel drivers network nforce ; SubInclude HAIKU_TOP src add-ons kernel drivers network pcnet ; @@ -23,7 +22,6 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network attansic_l1 ; SubInclude HAIKU_TOP src add-ons kernel drivers network attansic_l2 ; SubInclude HAIKU_TOP src add-ons kernel drivers network ar81xx ; SubInclude HAIKU_TOP src add-ons kernel drivers network rtl81xx ; -SubInclude HAIKU_TOP src add-ons kernel drivers network vt612x ; SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm440x ; SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm570x ; @@ -31,10 +29,12 @@ SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm570x ; # FreeBSD 8 drivers SubInclude HAIKU_TOP src add-ons kernel drivers network 3com ; SubInclude HAIKU_TOP src add-ons kernel drivers network atheros813x ; +SubInclude HAIKU_TOP src add-ons kernel drivers network dec21xxx ; SubInclude HAIKU_TOP src add-ons kernel drivers network ipro100 ; SubInclude HAIKU_TOP src add-ons kernel drivers network ipro1000 ; -SubInclude HAIKU_TOP src add-ons kernel drivers network dec21xxx ; +SubInclude HAIKU_TOP src add-ons kernel drivers network jmicron2x0 ; SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8139 ; +SubInclude HAIKU_TOP src add-ons kernel drivers network vt612x ; SubInclude HAIKU_TOP src add-ons kernel drivers network wlan ; SubInclude HAIKU_TOP src add-ons kernel drivers network wwan ; diff --git a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jme.c b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jme.c index 36f457bfd1..b9cd8b3360 100644 --- a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jme.c +++ b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jme.c @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: src/sys/dev/jme/if_jme.c,v 1.10 2008/12/04 02:16:53 yongari Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/jme/if_jme.c,v 1.11.2.8.2.1 2010/12/21 17:09:25 kensmith Exp $"); #include #include @@ -201,13 +201,6 @@ static struct resource_spec jme_irq_spec_legacy[] = { static struct resource_spec jme_irq_spec_msi[] = { { SYS_RES_IRQ, 1, RF_ACTIVE }, - { SYS_RES_IRQ, 2, RF_ACTIVE }, - { SYS_RES_IRQ, 3, RF_ACTIVE }, - { SYS_RES_IRQ, 4, RF_ACTIVE }, - { SYS_RES_IRQ, 5, RF_ACTIVE }, - { SYS_RES_IRQ, 6, RF_ACTIVE }, - { SYS_RES_IRQ, 7, RF_ACTIVE }, - { SYS_RES_IRQ, 8, RF_ACTIVE }, { -1, 0, 0 } }; @@ -224,13 +217,8 @@ jme_miibus_readreg(device_t dev, int phy, int reg) sc = device_get_softc(dev); /* For FPGA version, PHY address 0 should be ignored. */ - if ((sc->jme_flags & JME_FLAG_FPGA) != 0) { - if (phy == 0) - return (0); - } else { - if (sc->jme_phyaddr != phy) - return (0); - } + if ((sc->jme_flags & JME_FLAG_FPGA) != 0 && phy == 0) + return (0); CSR_WRITE_4(sc, JME_SMI, SMI_OP_READ | SMI_OP_EXECUTE | SMI_PHY_ADDR(phy) | SMI_REG_ADDR(reg)); @@ -260,13 +248,8 @@ jme_miibus_writereg(device_t dev, int phy, int reg, int val) sc = device_get_softc(dev); /* For FPGA version, PHY address 0 should be ignored. */ - if ((sc->jme_flags & JME_FLAG_FPGA) != 0) { - if (phy == 0) - return (0); - } else { - if (sc->jme_phyaddr != phy) - return (0); - } + if ((sc->jme_flags & JME_FLAG_FPGA) != 0 && phy == 0) + return (0); CSR_WRITE_4(sc, JME_SMI, SMI_OP_WRITE | SMI_OP_EXECUTE | ((val << SMI_DATA_SHIFT) & SMI_DATA_MASK) | @@ -306,6 +289,10 @@ jme_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) sc = ifp->if_softc; JME_LOCK(sc); + if ((ifp->if_flags & IFF_UP) == 0) { + JME_UNLOCK(sc); + return; + } mii = device_get_softc(sc->jme_miibus); mii_pollstat(mii); @@ -461,7 +448,7 @@ jme_reg_macaddr(struct jme_softc *sc) "generating fake ethernet address.\n"); par0 = arc4random(); /* Set OUI to JMicron. */ - sc->jme_eaddr[0] = 0x00; + sc->jme_eaddr[0] = 0x02; /* U/L bit set. */ sc->jme_eaddr[1] = 0x1B; sc->jme_eaddr[2] = 0x8C; sc->jme_eaddr[3] = (par0 >> 16) & 0xff; @@ -592,11 +579,16 @@ jme_attach(device_t dev) device_printf(dev, "MSI count : %d\n", msic); } + /* Use 1 MSI/MSI-X. */ + if (msixc > 1) + msixc = 1; + if (msic > 1) + msic = 1; /* Prefer MSIX over MSI. */ if (msix_disable == 0 || msi_disable == 0) { - if (msix_disable == 0 && msixc == JME_MSIX_MESSAGES && + if (msix_disable == 0 && msixc > 0 && pci_alloc_msix(dev, &msixc) == 0) { - if (msic == JME_MSIX_MESSAGES) { + if (msixc == 1) { device_printf(dev, "Using %d MSIX messages.\n", msixc); sc->jme_flags |= JME_FLAG_MSIX; @@ -605,9 +597,8 @@ jme_attach(device_t dev) pci_release_msi(dev); } if (msi_disable == 0 && (sc->jme_flags & JME_FLAG_MSIX) == 0 && - msic == JME_MSI_MESSAGES && - pci_alloc_msi(dev, &msic) == 0) { - if (msic == JME_MSI_MESSAGES) { + msic > 0 && pci_alloc_msi(dev, &msic) == 0) { + if (msic == 1) { device_printf(dev, "Using %d MSI messages.\n", msic); sc->jme_flags |= JME_FLAG_MSI; @@ -747,9 +738,11 @@ jme_attach(device_t dev) ifp->if_capenable = ifp->if_capabilities; /* Set up MII bus. */ - if ((error = mii_phy_probe(dev, &sc->jme_miibus, jme_mediachange, - jme_mediastatus)) != 0) { - device_printf(dev, "no PHY found!\n"); + error = mii_attach(dev, &sc->jme_miibus, ifp, jme_mediachange, + jme_mediastatus, BMSR_DEFCAPMASK, sc->jme_phyaddr, MII_OFFSET_ANY, + MIIF_DOPAUSE); + if (error != 0) { + device_printf(dev, "attaching PHYs failed\n"); goto fail; } @@ -779,7 +772,7 @@ jme_attach(device_t dev) /* VLAN capability setup */ ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | - IFCAP_VLAN_HWCSUM; + IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO; ifp->if_capenable = ifp->if_capabilities; /* Tell the upper layer(s) we support long frames. */ @@ -798,13 +791,7 @@ jme_attach(device_t dev) taskqueue_start_threads(&sc->jme_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->jme_dev)); - if ((sc->jme_flags & JME_FLAG_MSIX) != 0) - msic = JME_MSIX_MESSAGES; - else if ((sc->jme_flags & JME_FLAG_MSI) != 0) - msic = JME_MSI_MESSAGES; - else - msic = 1; - for (i = 0; i < msic; i++) { + for (i = 0; i < 1; i++) { error = bus_setup_intr(dev, sc->jme_irq[i], INTR_TYPE_NET | INTR_MPSAFE, jme_intr, NULL, sc, &sc->jme_intrhand[i]); @@ -832,7 +819,7 @@ jme_detach(device_t dev) { struct jme_softc *sc; struct ifnet *ifp; - int i, msic; + int i; sc = device_get_softc(dev); @@ -867,14 +854,7 @@ jme_detach(device_t dev) sc->jme_ifp = NULL; } - msic = 1; - if ((sc->jme_flags & JME_FLAG_MSIX) != 0) - msic = JME_MSIX_MESSAGES; - else if ((sc->jme_flags & JME_FLAG_MSI) != 0) - msic = JME_MSI_MESSAGES; - else - msic = 1; - for (i = 0; i < msic; i++) { + for (i = 0; i < 1; i++) { if (sc->jme_intrhand[i] != NULL) { bus_teardown_intr(dev, sc->jme_irq[i], sc->jme_intrhand[i]); @@ -1585,8 +1565,10 @@ jme_resume(device_t dev) pmc + PCIR_POWER_STATUS, pmstat, 2); } ifp = sc->jme_ifp; - if ((ifp->if_flags & IFF_UP) != 0) + if ((ifp->if_flags & IFF_UP) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); + } JME_UNLOCK(sc); @@ -1659,11 +1641,12 @@ jme_encap(struct jme_softc *sc, struct mbuf **m_head) *m_head = NULL; return (ENOBUFS); } - tcp = (struct tcphdr *)(mtod(m, char *) + poff); /* * Reset IP checksum and recompute TCP pseudo * checksum that NDIS specification requires. */ + ip = (struct ip *)(mtod(m, char *) + ip_off); + tcp = (struct tcphdr *)(mtod(m, char *) + poff); ip->ip_sum = 0; if (poff + (tcp->th_off << 2) == m->m_pkthdr.len) { tcp->th_sum = in_pseudo(ip->ip_src.s_addr, @@ -1861,6 +1844,7 @@ jme_watchdog(struct jme_softc *sc) if ((sc->jme_flags & JME_FLAG_LINK) == 0) { if_printf(sc->jme_ifp, "watchdog timeout (missed link)\n"); ifp->if_oerrors++; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); return; } @@ -1875,6 +1859,7 @@ jme_watchdog(struct jme_softc *sc) if_printf(sc->jme_ifp, "watchdog timeout\n"); ifp->if_oerrors++; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue(sc->jme_tq, &sc->jme_tx_task); @@ -1917,8 +1902,10 @@ jme_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) VLAN_CAPABILITIES(ifp); } ifp->if_mtu = ifr->ifr_mtu; - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; jme_init_locked(sc); + } JME_UNLOCK(sc); } break; @@ -1990,6 +1977,9 @@ jme_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if ((mask & IFCAP_VLAN_HWCSUM) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; + if ((mask & IFCAP_VLAN_HWTSO) != 0 && + (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0) + ifp->if_capenable ^= IFCAP_VLAN_HWTSO; if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && (IFCAP_VLAN_HWTAGGING & ifp->if_capabilities) != 0) { ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; @@ -2034,12 +2024,10 @@ jme_mac_config(struct jme_softc *sc) txmac &= ~(TXMAC_COLL_ENB | TXMAC_CARRIER_SENSE | TXMAC_BACKOFF | TXMAC_CARRIER_EXT | TXMAC_FRAME_BURST); -#ifdef notyet if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) txpause |= TXPFC_PAUSE_ENB; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) rxmac |= RXMAC_FC_ENB; -#endif /* Disable retry transmit timer/retry limit. */ CSR_WRITE_4(sc, JME_TXTRHD, CSR_READ_4(sc, JME_TXTRHD) & ~(TXTRHD_RT_PERIOD_ENB | TXTRHD_RT_LIMIT_ENB)); @@ -2642,6 +2630,8 @@ jme_init_locked(struct jme_softc *sc) ifp = sc->jme_ifp; mii = device_get_softc(sc->jme_miibus); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; /* * Cancel any pending I/O. */ @@ -3122,7 +3112,7 @@ jme_set_filter(struct jme_softc *sc) rxcfg |= RXMAC_MULTICAST; bzero(mchash, sizeof(mchash)); - IF_ADDR_LOCK(ifp); + if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &sc->jme_ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; @@ -3135,7 +3125,7 @@ jme_set_filter(struct jme_softc *sc) /* Set the corresponding bit in the hash table. */ mchash[crc >> 5] |= 1 << (crc & 0x1f); } - IF_ADDR_UNLOCK(ifp); + if_maddr_runlock(ifp); CSR_WRITE_4(sc, JME_MAR0, mchash[0]); CSR_WRITE_4(sc, JME_MAR1, mchash[1]); diff --git a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmereg.h b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmereg.h index 7040c4b828..ee2b2f674f 100644 --- a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmereg.h +++ b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmereg.h @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/jme/if_jmereg.h,v 1.6 2008/12/04 02:16:53 yongari Exp $ + * $FreeBSD: src/sys/dev/jme/if_jmereg.h,v 1.6.2.2.2.1 2010/12/21 17:09:25 kensmith Exp $ */ #ifndef _IF_JMEREG_H @@ -275,8 +275,8 @@ #define RXCSR_RXQ2 2 #define RXCSR_RXQ3 3 #define RXCSR_DESC_RT_CNT(x) \ - ((((x) / 4) << RXCSR_DESC_RT_CNT_SHIFT) & RXCSR_DESC_RT_CNT_MASK) -#define RXCSR_DESC_RT_CNT_DEFAULT 32 + (((x) << RXCSR_DESC_RT_CNT_SHIFT) & RXCSR_DESC_RT_CNT_MASK) +#define RXCSR_DESC_RT_CNT_DEFAULT 0 /* Rx queue descriptor base address. 16bytes alignment needed. */ #define JME_RXDBA_LO 0x0024 diff --git a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmevar.h b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmevar.h index 9f9b3895ec..b71cdf7be6 100644 --- a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmevar.h +++ b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/jme/if_jmevar.h @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/jme/if_jmevar.h,v 1.3 2008/12/04 02:16:53 yongari Exp $ + * $FreeBSD: src/sys/dev/jme/if_jmevar.h,v 1.3.2.1.6.1 2010/12/21 17:09:25 kensmith Exp $ */ #ifndef _IF_JMEVAR_H diff --git a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphy.c b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphy.c index 4fba75d9ab..53b09b8842 100644 --- a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphy.c +++ b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphy.c @@ -26,7 +26,7 @@ */ #include -__FBSDID("$FreeBSD: src/sys/dev/mii/jmphy.c,v 1.1 2008/05/27 01:16:40 yongari Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/mii/jmphy.c,v 1.1.6.5.2.1 2010/12/21 17:09:25 kensmith Exp $"); /* * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY. @@ -50,11 +50,11 @@ __FBSDID("$FreeBSD: src/sys/dev/mii/jmphy.c,v 1.1 2008/05/27 01:16:40 yongari Ex #include "miibus_if.h" -static int jmphy_probe(device_t); -static int jmphy_attach(device_t); +static int jmphy_probe(device_t); +static int jmphy_attach(device_t); static void jmphy_reset(struct mii_softc *); static uint16_t jmphy_anar(struct ifmedia_entry *); -static int jmphy_auto(struct mii_softc *, struct ifmedia_entry *); +static int jmphy_setmedia(struct mii_softc *, struct ifmedia_entry *); struct jmphy_softc { struct mii_softc mii_sc; @@ -109,16 +109,15 @@ jmphy_attach(device_t dev) sc = &jsc->mii_sc; ma = device_get_ivars(dev); sc->mii_dev = device_get_parent(dev); - mii = device_get_softc(sc->mii_dev); + mii = ma->mii_data; LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); - sc->mii_inst = mii->mii_instance; + sc->mii_flags = miibus_get_flags(dev); + sc->mii_inst = mii->mii_instance++; sc->mii_phy = ma->mii_phyno; sc->mii_service = jmphy_service; sc->mii_pdata = mii; - mii->mii_instance++; - jsc->mii_oui = MII_OUI(ma->mii_id1, ma->mii_id2); jsc->mii_model = MII_MODEL(ma->mii_id2); jsc->mii_rev = MII_REV(ma->mii_id2); @@ -136,52 +135,30 @@ jmphy_attach(device_t dev) printf("\n"); MIIBUS_MEDIAINIT(sc->mii_dev); - return(0); + return (0); } static int jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) { struct ifmedia_entry *ife = mii->mii_media.ifm_cur; - uint16_t bmcr; 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) { - bmcr = PHY_READ(sc, MII_BMCR); - PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); - return (0); - } - /* * If the interface is not up, don't do anything. */ if ((mii->mii_ifp->if_flags & IFF_UP) == 0) break; - if (jmphy_auto(sc, ife) != EJUSTRETURN) + if (jmphy_setmedia(sc, ife) != EJUSTRETURN) 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? */ @@ -209,7 +186,7 @@ jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) return (0); sc->mii_ticks = 0; - jmphy_auto(sc, ife); + (void)jmphy_setmedia(sc, ife); break; } @@ -274,16 +251,14 @@ jmphy_status(struct mii_softc *sc) } if ((ssr & JMPHY_SSR_DUPLEX) != 0) - mii->mii_media_active |= IFM_FDX; + mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); else mii->mii_media_active |= IFM_HDX; - /* XXX Flow-control. */ -#ifdef notyet + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0) mii->mii_media_active |= IFM_ETH_MASTER; } -#endif } static void @@ -332,7 +307,7 @@ jmphy_anar(struct ifmedia_entry *ife) } static int -jmphy_auto(struct mii_softc *sc, struct ifmedia_entry *ife) +jmphy_setmedia(struct mii_softc *sc, struct ifmedia_entry *ife) { uint16_t anar, bmcr, gig; @@ -359,17 +334,18 @@ jmphy_auto(struct mii_softc *sc, struct ifmedia_entry *ife) bmcr |= BMCR_LOOP; anar = jmphy_anar(ife); - /* XXX Always advertise pause capability. */ - anar |= (3 << 10); + if (((IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO || + (ife->ifm_media & IFM_FDX) != 0) && + (ife->ifm_media & IFM_FLOW) != 0) || + (sc->mii_flags & MIIF_FORCEPAUSE) != 0) + anar |= ANAR_PAUSE_TOWARDS; if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { -#ifdef notyet - struct mii_data *mii; - - mii = sc->mii_pdata; - if ((mii->mii_media.ifm_media & IFM_ETH_MASTER) != 0) - gig |= GTCR_MAN_MS | GTCR_MAN_ADV; -#endif + if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { + gig |= GTCR_MAN_MS; + if ((ife->ifm_media & IFM_ETH_MASTER) != 0) + gig |= GTCR_ADV_MS; + } PHY_WRITE(sc, MII_100T2CR, gig); } PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA); diff --git a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphyreg.h b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphyreg.h index fa98022620..d2a3e34075 100644 --- a/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphyreg.h +++ b/src/add-ons/kernel/drivers/network/jmicron2x0/dev/mii/jmphyreg.h @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mii/jmphyreg.h,v 1.1 2008/05/27 01:16:40 yongari Exp $ + * $FreeBSD: src/sys/dev/mii/jmphyreg.h,v 1.1.6.1.6.1 2010/12/21 17:09:25 kensmith Exp $ */ #ifndef _DEV_MII_JMPHYREG_H_ diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphy.c b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphy.c index e437af27d0..b0048ff219 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphy.c +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphy.c @@ -28,15 +28,13 @@ * 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: src/sys/dev/mii/ciphy.c,v 1.2 2005/01/06 01:42:55 imp Exp $ */ #include -__FBSDID("$FreeBSD: src/sys/dev/mii/ciphy.c,v 1.2 2005/01/06 01:42:55 imp Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/mii/ciphy.c,v 1.17.2.5.2.1 2010/12/21 17:09:25 kensmith Exp $"); /* - * Driver for the Cicada CS8201 10/100/1000 copper PHY. + * Driver for the Cicada/Vitesse CS/VSC8xxx 10/100/1000 copper PHY. */ #include @@ -46,8 +44,6 @@ __FBSDID("$FreeBSD: src/sys/dev/mii/ciphy.c,v 1.2 2005/01/06 01:42:55 imp Exp $" #include #include -#include - #include #include #include @@ -61,9 +57,7 @@ __FBSDID("$FreeBSD: src/sys/dev/mii/ciphy.c,v 1.2 2005/01/06 01:42:55 imp Exp $" #include "miibus_if.h" #include -/* -#include -*/ + static int ciphy_probe(device_t); static int ciphy_attach(device_t); @@ -91,40 +85,26 @@ static void ciphy_status(struct mii_softc *); static void ciphy_reset(struct mii_softc *); static void ciphy_fixup(struct mii_softc *); +static const struct mii_phydesc ciphys[] = { + MII_PHY_DESC(CICADA, CS8201), + MII_PHY_DESC(CICADA, CS8201A), + MII_PHY_DESC(CICADA, CS8201B), + MII_PHY_DESC(CICADA, CS8204), + MII_PHY_DESC(CICADA, VSC8211), + MII_PHY_DESC(CICADA, CS8244), + MII_PHY_DESC(VITESSE, VSC8601), + MII_PHY_END +}; + static int -ciphy_probe(dev) - device_t dev; +ciphy_probe(device_t dev) { - struct mii_attach_args *ma; - ma = device_get_ivars(dev); - -device_printf(dev, "OUI: %x\n", MII_OUI(ma->mii_id1, ma->mii_id2)); -device_printf(dev, "MODEL: %x\n", MII_MODEL(ma->mii_id2)); - 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); + return (mii_phy_dev_probe(dev, ciphys, BUS_PROBE_DEFAULT)); } static int -ciphy_attach(dev) - device_t dev; +ciphy_attach(device_t dev) { struct mii_softc *sc; struct mii_attach_args *ma; @@ -133,21 +113,20 @@ ciphy_attach(dev) sc = device_get_softc(dev); ma = device_get_ivars(dev); sc->mii_dev = device_get_parent(dev); - mii = device_get_softc(sc->mii_dev); + mii = ma->mii_data; LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); - sc->mii_inst = mii->mii_instance; + sc->mii_flags = miibus_get_flags(dev); + 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; + 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, " "); @@ -155,38 +134,20 @@ ciphy_attach(dev) printf("\n"); MIIBUS_MEDIAINIT(sc->mii_dev); - return(0); + return (0); } static int -ciphy_service(sc, mii, cmd) - struct mii_softc *sc; - struct mii_data *mii; - int cmd; +ciphy_service(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. */ @@ -204,7 +165,7 @@ ciphy_service(sc, mii, cmd) if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN) return (0); #endif - (void) mii_phy_auto(sc); + (void)mii_phy_auto(sc); break; case IFM_1000_T: speed = CIPHY_S1000; @@ -226,45 +187,26 @@ setit: 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) + if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) break; + gig |= CIPHY_1000CTL_MSE; + if ((ife->ifm_media & IFM_ETH_MASTER) != 0 || + (mii->mii_ifp->if_flags & IFF_LINK0) != 0) + gig |= CIPHY_1000CTL_MSC; 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); - } + speed | CIPHY_BMCR_AUTOEN | CIPHY_BMCR_STARTNEG); break; case IFM_NONE: - PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN); + 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? */ @@ -286,15 +228,18 @@ setit: if (reg & BMSR_LINK) break; - /* - * Only retry autonegotiation every 5 seconds. - */ - if (++sc->mii_ticks <= 5/*10*/) + /* Announce link loss right after it happens. */ + if (++sc->mii_ticks == 0) break; - + /* + * Only retry autonegotiation every mii_anegticks seconds. + */ + if (sc->mii_ticks <= sc->mii_anegticks) + break; + sc->mii_ticks = 0; mii_phy_auto(sc); - return (0); + break; } /* Update the media status. */ @@ -304,7 +249,7 @@ setit: * 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 || + if (sc->mii_media_active != mii->mii_media_active || sc->mii_media_status != mii->mii_media_status || cmd == MII_MEDIACHG) { ciphy_fixup(sc); @@ -314,8 +259,7 @@ setit: } static void -ciphy_status(sc) - struct mii_softc *sc; +ciphy_status(struct mii_softc *sc) { struct mii_data *mii = sc->mii_pdata; int bmsr, bmcr; @@ -360,17 +304,20 @@ ciphy_status(sc) if (bmsr & CIPHY_AUXCSR_FDX) mii->mii_media_active |= IFM_FDX; + else + mii->mii_media_active |= IFM_HDX; - return; + if ((IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) && + (PHY_READ(sc, CIPHY_MII_1000STS) & CIPHY_1000STS_MSR) != 0) + mii->mii_media_active |= IFM_ETH_MASTER; } static void ciphy_reset(struct mii_softc *sc) { + mii_phy_reset(sc); DELAY(1000); - - return; } #define PHY_SETBIT(x, y, z) \ @@ -383,12 +330,30 @@ ciphy_fixup(struct mii_softc *sc) { uint16_t model; uint16_t status, speed; + uint16_t val; model = MII_MODEL(PHY_READ(sc, CIPHY_MII_PHYIDR2)); status = PHY_READ(sc, CIPHY_MII_AUXCSR); speed = status & CIPHY_AUXCSR_SPEED; + if (strcmp(device_get_name(device_get_parent(sc->mii_dev)), + "nfe") == 0) { + /* need to set for 2.5V RGMII for NVIDIA adapters */ + val = PHY_READ(sc, CIPHY_MII_ECTL1); + val &= ~(CIPHY_ECTL1_IOVOL | CIPHY_ECTL1_INTSEL); + val |= (CIPHY_IOVOL_2500MV | CIPHY_INTSEL_RGMII); + PHY_WRITE(sc, CIPHY_MII_ECTL1, val); + /* From Linux. */ + val = PHY_READ(sc, CIPHY_MII_AUXCSR); + val |= CIPHY_AUXCSR_MDPPS; + PHY_WRITE(sc, CIPHY_MII_AUXCSR, val); + val = PHY_READ(sc, CIPHY_MII_10BTCSR); + val |= CIPHY_10BTCSR_ECHO; + PHY_WRITE(sc, CIPHY_MII_10BTCSR, val); + } + switch (model) { + case MII_MODEL_CICADA_CS8204: case MII_MODEL_CICADA_CS8201: /* Turn off "aux mode" (whatever that means) */ @@ -424,12 +389,14 @@ ciphy_fixup(struct mii_softc *sc) PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); } + break; + case MII_MODEL_CICADA_VSC8211: + case MII_MODEL_CICADA_CS8244: + case MII_MODEL_VITESSE_VSC8601: break; default: device_printf(sc->mii_dev, "unknown CICADA PHY model %x\n", model); break; } - - return; } diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphyreg.h b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphyreg.h index 727441ab1b..886b7b8fff 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphyreg.h +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ciphyreg.h @@ -29,7 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/mii/ciphyreg.h,v 1.2 2005/01/06 01:42:55 imp Exp $ + * $FreeBSD: src/sys/dev/mii/ciphyreg.h,v 1.3.10.1.6.1 2010/12/21 17:09:25 kensmith Exp $ */ #ifndef _DEV_MII_CIPHYREG_H_ @@ -251,6 +251,16 @@ /* Extended PHY control register #1 */ #define CIPHY_MII_ECTL1 0x17 #define CIPHY_ECTL1_ACTIPHY 0x0020 /* Enable ActiPHY power saving */ +#define CIPHY_ECTL1_IOVOL 0x0e00 /* MAC interface and I/O voltage select */ +#define CIPHY_ECTL1_INTSEL 0xf000 /* select MAC interface */ + +#define CIPHY_IOVOL_3300MV 0x0000 /* 3.3V for I/O pins */ +#define CIPHY_IOVOL_2500MV 0x0200 /* 2.5V for I/O pins */ + +#define CIPHY_INTSEL_GMII 0x0000 /* GMII/MII */ +#define CIPHY_INTSEL_RGMII 0x1000 +#define CIPHY_INTSEL_TBI 0x2000 +#define CIPHY_INTSEL_RTBI 0x3000 /* Extended PHY control register #2 */ #define CIPHY_MII_ECTL2 0x18 diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy.c b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy.c index 10347825bf..ad59059aad 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy.c +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy.c @@ -16,13 +16,6 @@ * 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 the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -48,11 +41,6 @@ * 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 Manuel Bouyer. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -67,7 +55,7 @@ */ #include -__FBSDID("$FreeBSD: src/sys/dev/mii/ukphy.c,v 1.20 2007/01/20 00:52:29 marius Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/mii/ukphy.c,v 1.20.10.6.2.1 2010/12/21 17:09:25 kensmith Exp $"); /* * driver for generic unknown PHYs @@ -134,7 +122,7 @@ ukphy_attach(device_t dev) sc = device_get_softc(dev); ma = device_get_ivars(dev); sc->mii_dev = device_get_parent(dev); - mii = device_get_softc(sc->mii_dev); + mii = ma->mii_data; LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); if (bootverbose) @@ -142,17 +130,17 @@ ukphy_attach(device_t dev) MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); - sc->mii_inst = mii->mii_instance; + sc->mii_flags = miibus_get_flags(dev); + sc->mii_inst = mii->mii_instance++; sc->mii_phy = ma->mii_phyno; sc->mii_service = ukphy_service; sc->mii_pdata = mii; - mii->mii_instance++; + sc->mii_flags |= MIIF_NOMANPAUSE; mii_phy_reset(sc); - sc->mii_capabilities = - PHY_READ(sc, MII_BMSR) & ma->mii_capmask; + 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, " "); @@ -168,29 +156,12 @@ ukphy_attach(device_t dev) static int ukphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) { - struct ifmedia_entry *ife = mii->mii_media.ifm_cur; - int reg; 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. */ @@ -201,11 +172,6 @@ ukphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) break; case MII_TICK: - /* - * If we're not currently selected, just return. - */ - if (IFM_INST(ife->ifm_media) != sc->mii_inst) - return (0); if (mii_phy_tick(sc) == EJUSTRETURN) return (0); break; diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy_subr.c b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy_subr.c index fb40b75c1d..73007e197b 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy_subr.c +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/mii/ukphy_subr.c @@ -16,13 +16,6 @@ * 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 the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -38,7 +31,7 @@ */ #include -__FBSDID("$FreeBSD: src/sys/dev/mii/ukphy_subr.c,v 1.8.8.1 2006/07/19 04:40:26 yongari Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/mii/ukphy_subr.c,v 1.10.2.4.2.1 2010/12/21 17:09:25 kensmith Exp $"); /* * Subroutines shared by the ukphy driver and other PHY drivers. @@ -111,19 +104,26 @@ ukphy_status(struct mii_softc *phy) mii->mii_media_active |= IFM_1000_T|IFM_FDX; else if ((gtcr & GTCR_ADV_1000THDX) && (gtsr & GTSR_LP_1000THDX)) - mii->mii_media_active |= IFM_1000_T; - else if (anlpar & ANLPAR_T4) - mii->mii_media_active |= IFM_100_T4; + mii->mii_media_active |= IFM_1000_T|IFM_HDX; else if (anlpar & ANLPAR_TX_FD) mii->mii_media_active |= IFM_100_TX|IFM_FDX; + else if (anlpar & ANLPAR_T4) + mii->mii_media_active |= IFM_100_T4|IFM_HDX; else if (anlpar & ANLPAR_TX) - mii->mii_media_active |= IFM_100_TX; + mii->mii_media_active |= IFM_100_TX|IFM_HDX; else if (anlpar & ANLPAR_10_FD) mii->mii_media_active |= IFM_10_T|IFM_FDX; else if (anlpar & ANLPAR_10) - mii->mii_media_active |= IFM_10_T; + mii->mii_media_active |= IFM_10_T|IFM_HDX; else mii->mii_media_active |= IFM_NONE; + + if ((mii->mii_media_active & IFM_1000_T) != 0 && + (gtsr & GTSR_MS_RES) != 0) + mii->mii_media_active |= IFM_ETH_MASTER; + + if ((mii->mii_media_active & IFM_FDX) != 0) + mii->mii_media_active |= mii_phy_flowstatus(phy); } else mii->mii_media_active = ife->ifm_media; } diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/Jamfile b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/Jamfile index f8b85176da..1d40cf74eb 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/Jamfile +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel drivers network vt612x dev vge ; SubDirCcFlags -Wall ; UseHeaders [ FDirName $(SUBDIR) .. .. ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network ] : true ; UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] : true ; UsePrivateHeaders net system ; diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/glue.c b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/glue.c index 5ca0397e86..80e058bf03 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/glue.c +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/glue.c @@ -4,7 +4,15 @@ */ +#include +#include #include +#include +#include + + +#include +#include HAIKU_FBSD_DRIVER_GLUE(vt612x, vge, pci); @@ -27,3 +35,25 @@ __haiku_select_miibus_driver(device_t dev) return __haiku_probe_miibus(dev, drivers); } + +int +__haiku_disable_interrupts(device_t dev) +{ + struct vge_softc *sc = device_get_softc(dev); + + if (CSR_READ_4(sc, VGE_ISR) == 0) + return 0; + + CSR_WRITE_4(sc, VGE_IMR, 0x00000000); + return 1; +} + + +void +__haiku_reenable_interrupts(device_t dev) +{ + struct vge_softc *sc = device_get_softc(dev); + + CSR_WRITE_4(sc, VGE_IMR, VGE_INTRS); +} + diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vge.c b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vge.c index 686dcded97..4e7a4bebc7 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vge.c +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vge.c @@ -31,7 +31,7 @@ */ #include -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: src/sys/dev/vge/if_vge.c,v 1.37.2.12.2.1 2010/12/21 17:09:25 kensmith Exp $"); /* * VIA Networking Technologies VT612x PCI gigabit ethernet NIC driver. @@ -93,7 +93,7 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include +#include #include #include @@ -128,68 +128,79 @@ MODULE_DEPEND(vge, miibus, 1, 1, 1); #define VGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) +/* Tunables */ +static int msi_disable = 0; +TUNABLE_INT("hw.vge.msi_disable", &msi_disable); + +/* + * The SQE error counter of MIB seems to report bogus value. + * Vendor's workaround does not seem to work on PCIe based + * controllers. Disable it until we find better workaround. + */ +#undef VGE_ENABLE_SQEERR + /* * Various supported device vendors/types and their names. */ static struct vge_type vge_devs[] = { { VIA_VENDORID, VIA_DEVICEID_61XX, - "VIA Networking Gigabit Ethernet" }, + "VIA Networking Velocity 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 int 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 int vge_shutdown (device_t); -static int vge_ifmedia_upd (struct ifnet *); -static void vge_ifmedia_sts (struct ifnet *, struct ifmediareq *); +static int vge_attach(device_t); +static int vge_detach(device_t); +static int vge_probe(device_t); +static int vge_resume(device_t); +static int vge_shutdown(device_t); +static int vge_suspend(device_t); +static void vge_cam_clear(struct vge_softc *); +static int vge_cam_set(struct vge_softc *, uint8_t *); +static void vge_clrwol(struct vge_softc *); +static void vge_discard_rxbuf(struct vge_softc *, int); +static int vge_dma_alloc(struct vge_softc *); +static void vge_dma_free(struct vge_softc *); +static void vge_dmamap_cb(void *, bus_dma_segment_t *, int, int); #ifdef VGE_EEPROM -static void vge_eeprom_getword (struct vge_softc *, int, u_int16_t *); +static void vge_eeprom_getword(struct vge_softc *, int, uint16_t *); #endif -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 *); -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 int vge_encap(struct vge_softc *, struct mbuf **); +#ifndef __NO_STRICT_ALIGNMENT +static __inline void + vge_fixup_rx(struct mbuf *); +#endif +static void vge_freebufs(struct vge_softc *); +static void vge_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int vge_ifmedia_upd(struct ifnet *); +static void vge_init(void *); +static void vge_init_locked(struct vge_softc *); +static void vge_intr(void *); +static void vge_intr_holdoff(struct vge_softc *); +static int vge_ioctl(struct ifnet *, u_long, caddr_t); +static void vge_link_statchg(void *); +static int vge_miibus_readreg(device_t, int, int); +static void vge_miibus_statchg(device_t); +static int vge_miibus_writereg(device_t, int, int, int); +static void vge_miipoll_start(struct vge_softc *); +static void vge_miipoll_stop(struct vge_softc *); +static int vge_newbuf(struct vge_softc *, int); +static void vge_read_eeprom(struct vge_softc *, caddr_t, int, int, int); +static void vge_reset(struct vge_softc *); +static int vge_rx_list_init(struct vge_softc *); +static int vge_rxeof(struct vge_softc *, int); +static void vge_rxfilter(struct vge_softc *); +static void vge_setvlan(struct vge_softc *); +static void vge_setwol(struct vge_softc *); +static void vge_start(struct ifnet *); +static void vge_start_locked(struct ifnet *); +static void vge_stats_clear(struct vge_softc *); +static void vge_stats_update(struct vge_softc *); +static void vge_stop(struct vge_softc *); +static void vge_sysctl_node(struct vge_softc *); +static int vge_tx_list_init(struct vge_softc *); +static void vge_txeof(struct vge_softc *); +static void vge_watchdog(void *); static device_method_t vge_methods[] = { /* Device interface */ @@ -223,41 +234,15 @@ static devclass_t vge_devclass; DRIVER_MODULE(vge, pci, vge_driver, vge_devclass, 0, 0); DRIVER_MODULE(miibus, vge, miibus_driver, miibus_devclass, 0, 0); -#ifdef __HAIKU__ -int -__haiku_disable_interrupts(device_t dev) -{ - struct vge_softc *sc = device_get_softc(dev); - - if (CSR_READ_4(sc, VGE_ISR) == 0) - return 0; - - CSR_WRITE_4(sc, VGE_IMR, 0x00000000); - return 1; -} - - -void -__haiku_reenable_interrupts(device_t dev) -{ - struct vge_softc *sc = device_get_softc(dev); - - CSR_WRITE_4(sc, VGE_IMR, VGE_INTRS); -} -#endif /* __HAIKU__ */ - #ifdef VGE_EEPROM /* * 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; +vge_eeprom_getword(struct vge_softc *sc, int addr, uint16_t *dest) { - register int i; - u_int16_t word = 0; + int i; + uint16_t word = 0; /* * Enter EEPROM embedded programming mode. In order to @@ -293,8 +278,6 @@ vge_eeprom_getword(sc, addr, dest) CSR_CLRBIT_1(sc, VGE_CHIPCFG2, VGE_CHIPCFG2_EELOAD); *dest = word; - - return; } #endif @@ -302,20 +285,15 @@ vge_eeprom_getword(sc, addr, dest) * 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; +vge_read_eeprom(struct vge_softc *sc, caddr_t dest, int off, int cnt, int swap) { - int i; + int i; #ifdef VGE_EEPROM - u_int16_t word = 0, *ptr; + uint16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { vge_eeprom_getword(sc, off + i, &word); - ptr = (u_int16_t *)(dest + (i * 2)); + ptr = (uint16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else @@ -328,10 +306,9 @@ vge_read_eeprom(sc, dest, off, cnt, swap) } static void -vge_miipoll_stop(sc) - struct vge_softc *sc; +vge_miipoll_stop(struct vge_softc *sc) { - int i; + int i; CSR_WRITE_1(sc, VGE_MIICMD, 0); @@ -343,15 +320,12 @@ vge_miipoll_stop(sc) 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; +vge_miipoll_start(struct vge_softc *sc) { - int i; + int i; /* First, make sure we're idle. */ @@ -383,25 +357,17 @@ vge_miipoll_start(sc) 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; +vge_miibus_readreg(device_t dev, int phy, int reg) { - struct vge_softc *sc; - int i; - u_int16_t rval = 0; + struct vge_softc *sc; + int i; + uint16_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. */ @@ -423,25 +389,18 @@ vge_miibus_readreg(dev, phy, reg) 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; +vge_miibus_writereg(device_t dev, int phy, int reg, int data) { - struct vge_softc *sc; - int i, rval = 0; + 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. */ @@ -466,16 +425,14 @@ vge_miibus_writereg(dev, phy, reg, data) } vge_miipoll_start(sc); - VGE_UNLOCK(sc); return (rval); } static void -vge_cam_clear(sc) - struct vge_softc *sc; +vge_cam_clear(struct vge_softc *sc) { - int i; + int i; /* * Turn off all the mask bits. This tells the chip @@ -500,19 +457,15 @@ vge_cam_clear(sc) 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; +vge_cam_set(struct vge_softc *sc, uint8_t *addr) { - int i, error = 0; + int i, error = 0; if (sc->vge_camidx == VGE_CAM_MAXADDRS) - return(ENOSPC); + return (ENOSPC); /* Select the CAM data page. */ CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL); @@ -560,37 +513,66 @@ fail: return (error); } +static void +vge_setvlan(struct vge_softc *sc) +{ + struct ifnet *ifp; + uint8_t cfg; + + VGE_LOCK_ASSERT(sc); + + ifp = sc->vge_ifp; + cfg = CSR_READ_1(sc, VGE_RXCFG); + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) + cfg |= VGE_VTAG_OPT2; + else + cfg &= ~VGE_VTAG_OPT2; + CSR_WRITE_1(sc, VGE_RXCFG, cfg); +} + /* * 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. + * we use the hash filter instead. */ static void -vge_setmulti(sc) - struct vge_softc *sc; +vge_rxfilter(struct vge_softc *sc) { - struct ifnet *ifp; - int error = 0/*, h = 0*/; - struct ifmultiaddr *ifma; - u_int32_t h, hashes[2] = { 0, 0 }; + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint32_t h, hashes[2]; + uint8_t rxcfg; + int error = 0; - ifp = sc->vge_ifp; + VGE_LOCK_ASSERT(sc); /* First, zot all the multicast entries. */ - vge_cam_clear(sc); - CSR_WRITE_4(sc, VGE_MAR0, 0); - CSR_WRITE_4(sc, VGE_MAR1, 0); + hashes[0] = 0; + hashes[1] = 0; + rxcfg = CSR_READ_1(sc, VGE_RXCTL); + rxcfg &= ~(VGE_RXCTL_RX_MCAST | VGE_RXCTL_RX_BCAST | + VGE_RXCTL_RX_PROMISC); /* - * If the user wants allmulti or promisc mode, enable reception - * of all multicast frames. + * Always allow VLAN oversized frames and frames for + * this host. */ - 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; + rxcfg |= VGE_RXCTL_RX_GIANT | VGE_RXCTL_RX_UCAST; + + ifp = sc->vge_ifp; + if ((ifp->if_flags & IFF_BROADCAST) != 0) + rxcfg |= VGE_RXCTL_RX_BCAST; + if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { + if ((ifp->if_flags & IFF_PROMISC) != 0) + rxcfg |= VGE_RXCTL_RX_PROMISC; + if ((ifp->if_flags & IFF_ALLMULTI) != 0) { + hashes[0] = 0xFFFFFFFF; + hashes[1] = 0xFFFFFFFF; + } + goto done; } + vge_cam_clear(sc); /* Now program new ones */ if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { @@ -616,20 +598,21 @@ vge_setmulti(sc) else hashes[1] |= (1 << (h - 32)); } - - CSR_WRITE_4(sc, VGE_MAR0, hashes[0]); - CSR_WRITE_4(sc, VGE_MAR1, hashes[1]); } if_maddr_runlock(ifp); - return; +done: + if (hashes[0] != 0 || hashes[1] != 0) + rxcfg |= VGE_RXCTL_RX_MCAST; + CSR_WRITE_4(sc, VGE_MAR0, hashes[0]); + CSR_WRITE_4(sc, VGE_MAR1, hashes[1]); + CSR_WRITE_1(sc, VGE_RXCTL, rxcfg); } static void -vge_reset(sc) - struct vge_softc *sc; +vge_reset(struct vge_softc *sc) { - register int i; + int i; CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_SOFTRESET); @@ -640,29 +623,12 @@ vge_reset(sc) } if (i == VGE_TIMEOUT) { - device_printf(sc->vge_dev, "soft reset timed out"); + device_printf(sc->vge_dev, "soft reset timed out\n"); 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; } /* @@ -670,10 +636,9 @@ vge_reset(sc) * IDs against our list and return a device name if we find a match. */ static int -vge_probe(dev) - device_t dev; +vge_probe(device_t dev) { - struct vge_type *t; + struct vge_type *t; t = vge_devs; @@ -689,249 +654,320 @@ vge_probe(dev) 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; +struct vge_dmamap_arg { + bus_addr_t vge_busaddr; +}; - if (error) +static void +vge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct vge_dmamap_arg *ctx; + + if (error != 0) return; - KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); - addr = arg; - *addr = segs->ds_addr; + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); - return; + ctx = (struct vge_dmamap_arg *)arg; + ctx->vge_busaddr = segs[0].ds_addr; } static int -vge_allocmem(dev, sc) - device_t dev; - struct vge_softc *sc; +vge_dma_alloc(struct vge_softc *sc) { - int error; - int nseg; - int i; + struct vge_dmamap_arg ctx; + struct vge_txdesc *txd; + struct vge_rxdesc *rxd; + bus_addr_t lowaddr, tx_ring_end, rx_ring_end; + int error, 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); + lowaddr = BUS_SPACE_MAXADDR; + +again: + /* Create parent ring tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->vge_dev),/* parent */ + 1, 0, /* algnmnt, boundary */ + lowaddr, /* 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->vge_cdata.vge_ring_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create parent DMA tag.\n"); + goto fail; } - /* - * 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); + /* Create tag for Tx ring. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_ring_tag,/* parent */ + VGE_TX_RING_ALIGN, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + VGE_TX_LIST_SZ, /* maxsize */ + 1, /* nsegments */ + VGE_TX_LIST_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_tx_ring_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate Tx ring DMA tag.\n"); + goto fail; } - /* Allocate DMA'able memory for the TX ring */ + /* Create tag for Rx ring. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_ring_tag,/* parent */ + VGE_RX_RING_ALIGN, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + VGE_RX_LIST_SZ, /* maxsize */ + 1, /* nsegments */ + VGE_RX_LIST_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_rx_ring_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate Rx ring DMA tag.\n"); + goto fail; + } - 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); + /* Allocate DMA'able memory and load the DMA map for Tx ring. */ + error = bus_dmamem_alloc(sc->vge_cdata.vge_tx_ring_tag, + (void **)&sc->vge_rdata.vge_tx_ring, + BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, + &sc->vge_cdata.vge_tx_ring_map); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate DMA'able memory for Tx ring.\n"); + goto fail; + } - /* Load the map for the TX ring. */ + ctx.vge_busaddr = 0; + error = bus_dmamap_load(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map, sc->vge_rdata.vge_tx_ring, + VGE_TX_LIST_SZ, vge_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0 || ctx.vge_busaddr == 0) { + device_printf(sc->vge_dev, + "could not load DMA'able memory for Tx ring.\n"); + goto fail; + } + sc->vge_rdata.vge_tx_ring_paddr = ctx.vge_busaddr; - 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); + /* Allocate DMA'able memory and load the DMA map for Rx ring. */ + error = bus_dmamem_alloc(sc->vge_cdata.vge_rx_ring_tag, + (void **)&sc->vge_rdata.vge_rx_ring, + BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, + &sc->vge_cdata.vge_rx_ring_map); + if (error != 0) { + device_printf(sc->vge_dev, + "could not allocate DMA'able memory for Rx ring.\n"); + goto fail; + } - /* Create DMA maps for TX buffers */ + ctx.vge_busaddr = 0; + error = bus_dmamap_load(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map, sc->vge_rdata.vge_rx_ring, + VGE_RX_LIST_SZ, vge_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0 || ctx.vge_busaddr == 0) { + device_printf(sc->vge_dev, + "could not load DMA'able memory for Rx ring.\n"); + goto fail; + } + sc->vge_rdata.vge_rx_ring_paddr = ctx.vge_busaddr; + /* Tx/Rx descriptor queue should reside within 4GB boundary. */ + tx_ring_end = sc->vge_rdata.vge_tx_ring_paddr + VGE_TX_LIST_SZ; + rx_ring_end = sc->vge_rdata.vge_rx_ring_paddr + VGE_RX_LIST_SZ; + if ((VGE_ADDR_HI(tx_ring_end) != + VGE_ADDR_HI(sc->vge_rdata.vge_tx_ring_paddr)) || + (VGE_ADDR_HI(rx_ring_end) != + VGE_ADDR_HI(sc->vge_rdata.vge_rx_ring_paddr)) || + VGE_ADDR_HI(tx_ring_end) != VGE_ADDR_HI(rx_ring_end)) { + device_printf(sc->vge_dev, "4GB boundary crossed, " + "switching to 32bit DMA address mode.\n"); + vge_dma_free(sc); + /* Limit DMA address space to 32bit and try again. */ + lowaddr = BUS_SPACE_MAXADDR_32BIT; + goto again; + } + + /* Create parent buffer tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->vge_dev),/* parent */ + 1, 0, /* algnmnt, boundary */ + VGE_BUF_DMA_MAXADDR, /* 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->vge_cdata.vge_buffer_tag); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create parent buffer DMA tag.\n"); + goto fail; + } + + /* Create tag for Tx buffers. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_buffer_tag,/* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * VGE_MAXTXSEGS, /* maxsize */ + VGE_MAXTXSEGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->vge_cdata.vge_tx_tag); + if (error != 0) { + device_printf(sc->vge_dev, "could not create Tx DMA tag.\n"); + goto fail; + } + + /* Create tag for Rx buffers. */ + error = bus_dma_tag_create(sc->vge_cdata.vge_buffer_tag,/* parent */ + VGE_RX_BUF_ALIGN, 0, /* algnmnt, 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->vge_cdata.vge_rx_tag); + if (error != 0) { + device_printf(sc->vge_dev, "could not create Rx DMA tag.\n"); + goto fail; + } + + /* 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); + txd = &sc->vge_cdata.vge_txdesc[i]; + txd->tx_m = NULL; + txd->tx_dmamap = NULL; + error = bus_dmamap_create(sc->vge_cdata.vge_tx_tag, 0, + &txd->tx_dmamap); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create Tx dmamap.\n"); + goto fail; } } - - /* - * 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); + /* Create DMA maps for Rx buffers. */ + if ((error = bus_dmamap_create(sc->vge_cdata.vge_rx_tag, 0, + &sc->vge_cdata.vge_rx_sparemap)) != 0) { + device_printf(sc->vge_dev, + "could not create spare Rx dmamap.\n"); + goto fail; } - - /* 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); + rxd = &sc->vge_cdata.vge_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_dmamap = NULL; + error = bus_dmamap_create(sc->vge_cdata.vge_rx_tag, 0, + &rxd->rx_dmamap); + if (error != 0) { + device_printf(sc->vge_dev, + "could not create Rx dmamap.\n"); + goto fail; } } - return (0); +fail: + return (error); +} + +static void +vge_dma_free(struct vge_softc *sc) +{ + struct vge_txdesc *txd; + struct vge_rxdesc *rxd; + int i; + + /* Tx ring. */ + if (sc->vge_cdata.vge_tx_ring_tag != NULL) { + if (sc->vge_cdata.vge_tx_ring_map) + bus_dmamap_unload(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map); + if (sc->vge_cdata.vge_tx_ring_map && + sc->vge_rdata.vge_tx_ring) + bus_dmamem_free(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_rdata.vge_tx_ring, + sc->vge_cdata.vge_tx_ring_map); + sc->vge_rdata.vge_tx_ring = NULL; + sc->vge_cdata.vge_tx_ring_map = NULL; + bus_dma_tag_destroy(sc->vge_cdata.vge_tx_ring_tag); + sc->vge_cdata.vge_tx_ring_tag = NULL; + } + /* Rx ring. */ + if (sc->vge_cdata.vge_rx_ring_tag != NULL) { + if (sc->vge_cdata.vge_rx_ring_map) + bus_dmamap_unload(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map); + if (sc->vge_cdata.vge_rx_ring_map && + sc->vge_rdata.vge_rx_ring) + bus_dmamem_free(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_rdata.vge_rx_ring, + sc->vge_cdata.vge_rx_ring_map); + sc->vge_rdata.vge_rx_ring = NULL; + sc->vge_cdata.vge_rx_ring_map = NULL; + bus_dma_tag_destroy(sc->vge_cdata.vge_rx_ring_tag); + sc->vge_cdata.vge_rx_ring_tag = NULL; + } + /* Tx buffers. */ + if (sc->vge_cdata.vge_tx_tag != NULL) { + for (i = 0; i < VGE_TX_DESC_CNT; i++) { + txd = &sc->vge_cdata.vge_txdesc[i]; + if (txd->tx_dmamap != NULL) { + bus_dmamap_destroy(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap); + txd->tx_dmamap = NULL; + } + } + bus_dma_tag_destroy(sc->vge_cdata.vge_tx_tag); + sc->vge_cdata.vge_tx_tag = NULL; + } + /* Rx buffers. */ + if (sc->vge_cdata.vge_rx_tag != NULL) { + for (i = 0; i < VGE_RX_DESC_CNT; i++) { + rxd = &sc->vge_cdata.vge_rxdesc[i]; + if (rxd->rx_dmamap != NULL) { + bus_dmamap_destroy(sc->vge_cdata.vge_rx_tag, + rxd->rx_dmamap); + rxd->rx_dmamap = NULL; + } + } + if (sc->vge_cdata.vge_rx_sparemap != NULL) { + bus_dmamap_destroy(sc->vge_cdata.vge_rx_tag, + sc->vge_cdata.vge_rx_sparemap); + sc->vge_cdata.vge_rx_sparemap = NULL; + } + bus_dma_tag_destroy(sc->vge_cdata.vge_rx_tag); + sc->vge_cdata.vge_rx_tag = NULL; + } + + if (sc->vge_cdata.vge_buffer_tag != NULL) { + bus_dma_tag_destroy(sc->vge_cdata.vge_buffer_tag); + sc->vge_cdata.vge_buffer_tag = NULL; + } + if (sc->vge_cdata.vge_ring_tag != NULL) { + bus_dma_tag_destroy(sc->vge_cdata.vge_ring_tag); + sc->vge_cdata.vge_ring_tag = NULL; + } } /* @@ -939,128 +975,158 @@ vge_allocmem(dev, sc) * setup and ethernet/BPF attach. */ static int -vge_attach(dev) - device_t dev; +vge_attach(device_t dev) { - u_char eaddr[ETHER_ADDR_LEN]; - struct vge_softc *sc; - struct ifnet *ifp; - int unit, error = 0, rid; + u_char eaddr[ETHER_ADDR_LEN]; + struct vge_softc *sc; + struct ifnet *ifp; + int error = 0, cap, i, msic, 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); + MTX_DEF); + callout_init_mtx(&sc->vge_watchdog, &sc->vge_mtx, 0); + /* * 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); + rid = PCIR_BAR(1); + sc->vge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); if (sc->vge_res == NULL) { - printf ("vge%d: couldn't map ports/memory\n", unit); + device_printf(dev, "couldn't map ports/memory\n"); error = ENXIO; goto fail; } - sc->vge_btag = rman_get_bustag(sc->vge_res); - sc->vge_bhandle = rman_get_bushandle(sc->vge_res); + if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) == 0) { + sc->vge_flags |= VGE_FLAG_PCIE; + sc->vge_expcap = cap; + } else + sc->vge_flags |= VGE_FLAG_JUMBO; + if (pci_find_extcap(dev, PCIY_PMG, &cap) == 0) { + sc->vge_flags |= VGE_FLAG_PMCAP; + sc->vge_pmcap = cap; + } + rid = 0; + msic = pci_msi_count(dev); + if (msi_disable == 0 && msic > 0) { + msic = 1; + if (pci_alloc_msi(dev, &msic) == 0) { + if (msic == 1) { + sc->vge_flags |= VGE_FLAG_MSI; + device_printf(dev, "Using %d MSI message\n", + msic); + rid = 1; + } else + pci_release_msi(dev); + } + } /* Allocate interrupt */ - rid = 0; - sc->vge_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, - 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); - + sc->vge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + ((sc->vge_flags & VGE_FLAG_MSI) ? 0 : RF_SHAREABLE) | RF_ACTIVE); if (sc->vge_irq == NULL) { - printf("vge%d: couldn't map interrupt\n", unit); + device_printf(dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } /* Reset the adapter. */ vge_reset(sc); + /* Reload EEPROM. */ + CSR_WRITE_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(dev, "EEPROM reload timed out\n"); + /* + * Clear PACPI as EEPROM reload will set the bit. Otherwise + * MAC will receive magic packet which in turn confuses + * controller. + */ + CSR_CLRBIT_1(sc, VGE_CHIPCFG0, VGE_CHIPCFG0_PACPI); /* * Get station address from the EEPROM. */ vge_read_eeprom(sc, (caddr_t)eaddr, VGE_EE_EADDR, 3, 0); - - sc->vge_unit = unit; - /* - * Allocate the parent bus DMA tag appropriate for PCI. + * Save configured PHY address. + * It seems the PHY address of PCIe controllers just + * reflects media jump strapping status so we assume the + * internal PHY address of PCIe controller is at 1. */ -#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 ((sc->vge_flags & VGE_FLAG_PCIE) != 0) + sc->vge_phyaddr = 1; + else + sc->vge_phyaddr = CSR_READ_1(sc, VGE_MIICFG) & + VGE_MIICFG_PHYADDR; + /* Clear WOL and take hardware from powerdown. */ + vge_clrwol(sc); + vge_sysctl_node(sc); + error = vge_dma_alloc(sc); if (error) goto fail; ifp = sc->vge_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - printf("vge%d: can not if_alloc()\n", sc->vge_unit); + device_printf(dev, "can not if_alloc()\n"); error = ENOSPC; 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; + error = mii_attach(dev, &sc->vge_miibus, ifp, vge_ifmedia_upd, + vge_ifmedia_sts, BMSR_DEFCAPMASK, sc->vge_phyaddr, MII_OFFSET_ANY, + 0); + if (error != 0) { + device_printf(dev, "attaching PHYs failed\n"); 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 = 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; + ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM | + IFCAP_VLAN_HWTAGGING; + if ((sc->vge_flags & VGE_FLAG_PMCAP) != 0) + ifp->if_capabilities |= IFCAP_WOL; ifp->if_capenable = ifp->if_capabilities; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif - ifp->if_watchdog = vge_watchdog; ifp->if_init = vge_init; - IFQ_SET_MAXLEN(&ifp->if_snd, VGE_IFQ_MAXLEN); - ifp->if_snd.ifq_drv_maxlen = VGE_IFQ_MAXLEN; + IFQ_SET_MAXLEN(&ifp->if_snd, VGE_TX_DESC_CNT - 1); + ifp->if_snd.ifq_drv_maxlen = VGE_TX_DESC_CNT - 1; IFQ_SET_READY(&ifp->if_snd); - TASK_INIT(&sc->vge_txtask, 0, vge_tx_task, ifp); - /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); + /* Tell the upper layer(s) we support long frames. */ + 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->vge_irq, INTR_TYPE_NET|INTR_MPSAFE, NULL, vge_intr, sc, &sc->vge_intrhand); if (error) { - printf("vge%d: couldn't set up irq\n", unit); + device_printf(dev, "couldn't set up irq\n"); ether_ifdetach(ifp); goto fail; } @@ -1080,12 +1146,10 @@ fail: * allocated. */ static int -vge_detach(dev) - device_t dev; +vge_detach(device_t dev) { - struct vge_softc *sc; - struct ifnet *ifp; - int i; + struct vge_softc *sc; + struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->vge_mtx), ("vge mutex not initialized")); @@ -1098,21 +1162,11 @@ vge_detach(dev) /* 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); + VGE_LOCK(sc); + vge_stop(sc); + VGE_UNLOCK(sc); + callout_drain(&sc->vge_watchdog); } if (sc->vge_miibus) device_delete_child(dev, sc->vge_miibus); @@ -1121,104 +1175,31 @@ vge_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); + bus_release_resource(dev, SYS_RES_IRQ, + sc->vge_flags & VGE_FLAG_MSI ? 1 : 0, sc->vge_irq); + if (sc->vge_flags & VGE_FLAG_MSI) + pci_release_msi(dev); if (sc->vge_res) bus_release_resource(dev, SYS_RES_MEMORY, - VGE_PCI_LOMEM, sc->vge_res); + PCIR_BAR(1), sc->vge_res); if (ifp) if_free(ifp); - /* 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_dma_free(sc); mtx_destroy(&sc->vge_mtx); return (0); } -static int -vge_newbuf(sc, idx, m) - struct vge_softc *sc; - int idx; - struct mbuf *m; +static void +vge_discard_rxbuf(struct vge_softc *sc, int prod) { - struct vge_dmaload_arg arg; - struct mbuf *n = NULL; - int i, error; + struct vge_rxdesc *rxd; + int i; - 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); - } + rxd = &sc->vge_cdata.vge_rxdesc[prod]; + rxd->rx_desc->vge_sts = 0; + rxd->rx_desc->vge_ctl = 0; /* * Note: the manual fails to document the fact that for @@ -1228,79 +1209,197 @@ vge_newbuf(sc, idx, m) * but we should not set the OWN bits until we're ready * to hand back 4 of them in one shot. */ + if ((prod % VGE_RXCHUNK) == (VGE_RXCHUNK - 1)) { + for (i = VGE_RXCHUNK; i > 0; i--) { + rxd->rx_desc->vge_sts = htole32(VGE_RDSTS_OWN); + rxd = rxd->rxd_prev; + } + sc->vge_cdata.vge_rx_commit += VGE_RXCHUNK; + } +} -#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; +static int +vge_newbuf(struct vge_softc *sc, int prod) +{ + struct vge_rxdesc *rxd; + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + int i, nsegs; + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); + /* + * This is part of an evil trick to deal with strict-alignment + * architectures. The VIA chip requires RX buffers to be aligned + * on 32-bit boundaries, but that will hose strict-alignment + * architectures. To get around this, we leave some empty space + * at the start of each buffer and for non-strict-alignment 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; + m_adj(m, VGE_RX_BUF_ALIGN); + + if (bus_dmamap_load_mbuf_sg(sc->vge_cdata.vge_rx_tag, + sc->vge_cdata.vge_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); + } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc->vge_cdata.vge_rxdesc[prod]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->vge_cdata.vge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->vge_cdata.vge_rx_tag, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc->vge_cdata.vge_rx_sparemap; + sc->vge_cdata.vge_rx_sparemap = map; + bus_dmamap_sync(sc->vge_cdata.vge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + + rxd->rx_desc->vge_sts = 0; + rxd->rx_desc->vge_ctl = 0; + rxd->rx_desc->vge_addrlo = htole32(VGE_ADDR_LO(segs[0].ds_addr)); + rxd->rx_desc->vge_addrhi = htole32(VGE_ADDR_HI(segs[0].ds_addr) | + (VGE_BUFLEN(segs[0].ds_len) << 16) | VGE_RXDESC_I); + + /* + * Note: the manual fails to document the fact that for + * proper operation, the driver needs to replenish 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. + */ + if ((prod % VGE_RXCHUNK) == (VGE_RXCHUNK - 1)) { + for (i = VGE_RXCHUNK; i > 0; i--) { + rxd->rx_desc->vge_sts = htole32(VGE_RDSTS_OWN); + rxd = rxd->rxd_prev; + } + sc->vge_cdata.vge_rx_commit += VGE_RXCHUNK; } - sc->vge_ldata.vge_rx_mbuf[idx] = m; + return (0); +} - bus_dmamap_sync(sc->vge_ldata.vge_mtag, - sc->vge_ldata.vge_rx_dmamap[idx], - BUS_DMASYNC_PREREAD); +static int +vge_tx_list_init(struct vge_softc *sc) +{ + struct vge_ring_data *rd; + struct vge_txdesc *txd; + int i; + + VGE_LOCK_ASSERT(sc); + + sc->vge_cdata.vge_tx_prodidx = 0; + sc->vge_cdata.vge_tx_considx = 0; + sc->vge_cdata.vge_tx_cnt = 0; + + rd = &sc->vge_rdata; + bzero(rd->vge_tx_ring, VGE_TX_LIST_SZ); + for (i = 0; i < VGE_TX_DESC_CNT; i++) { + txd = &sc->vge_cdata.vge_txdesc[i]; + txd->tx_m = NULL; + txd->tx_desc = &rd->vge_tx_ring[i]; + } + + bus_dmamap_sync(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } static int -vge_tx_list_init(sc) - struct vge_softc *sc; +vge_rx_list_init(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 *))); + struct vge_ring_data *rd; + struct vge_rxdesc *rxd; + int i; - 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; + VGE_LOCK_ASSERT(sc); - 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; + sc->vge_cdata.vge_rx_prodidx = 0; + sc->vge_cdata.vge_head = NULL; + sc->vge_cdata.vge_tail = NULL; + sc->vge_cdata.vge_rx_commit = 0; + rd = &sc->vge_rdata; + bzero(rd->vge_rx_ring, VGE_RX_LIST_SZ); for (i = 0; i < VGE_RX_DESC_CNT; i++) { - if (vge_newbuf(sc, i, NULL) == ENOBUFS) + rxd = &sc->vge_cdata.vge_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_desc = &rd->vge_rx_ring[i]; + if (i == 0) + rxd->rxd_prev = + &sc->vge_cdata.vge_rxdesc[VGE_RX_DESC_CNT - 1]; + else + rxd->rxd_prev = &sc->vge_cdata.vge_rxdesc[i - 1]; + if (vge_newbuf(sc, i) != 0) return (ENOBUFS); } - /* Flush the RX descriptors */ + bus_dmamap_sync(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - 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; + sc->vge_cdata.vge_rx_commit = 0; return (0); } -#ifdef VGE_FIXUP_RX -static __inline void -vge_fixup_rx(m) - struct mbuf *m; +static void +vge_freebufs(struct vge_softc *sc) { - int i; - uint16_t *src, *dst; + struct vge_txdesc *txd; + struct vge_rxdesc *rxd; + struct ifnet *ifp; + int i; + + VGE_LOCK_ASSERT(sc); + + ifp = sc->vge_ifp; + /* + * Free RX and TX mbufs still in the queues. + */ + for (i = 0; i < VGE_RX_DESC_CNT; i++) { + rxd = &sc->vge_cdata.vge_rxdesc[i]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->vge_cdata.vge_rx_tag, + rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->vge_cdata.vge_rx_tag, + rxd->rx_dmamap); + m_freem(rxd->rx_m); + rxd->rx_m = NULL; + } + } + + for (i = 0; i < VGE_TX_DESC_CNT; i++) { + txd = &sc->vge_cdata.vge_txdesc[i]; + if (txd->tx_m != NULL) { + bus_dmamap_sync(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; + ifp->if_oerrors++; + } + } +} + +#ifndef __NO_STRICT_ALIGNMENT +static __inline void +vge_fixup_rx(struct mbuf *m) +{ + int i; + uint16_t *src, *dst; src = mtod(m, uint16_t *); dst = src - 1; @@ -1309,8 +1408,6 @@ vge_fixup_rx(m) *dst++ = *src++; m->m_data -= ETHER_ALIGN; - - return; } #endif @@ -1319,49 +1416,37 @@ vge_fixup_rx(m) * been fragmented across multiple 2K mbuf cluster buffers. */ static int -vge_rxeof(sc) - struct vge_softc *sc; +vge_rxeof(struct vge_softc *sc, int count) { - struct mbuf *m; - struct ifnet *ifp; - int i, total_len; - int lim = 0; - struct vge_rx_desc *cur_rx; - u_int32_t rxstat, rxctl; + struct mbuf *m; + struct ifnet *ifp; + int prod, prog, total_len; + struct vge_rxdesc *rxd; + struct vge_rx_desc *cur_rx; + uint32_t rxstat, rxctl; VGE_LOCK_ASSERT(sc); + ifp = sc->vge_ifp; - i = sc->vge_ldata.vge_rx_prodidx; - /* Invalidate the descriptor memory */ + bus_dmamap_sync(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - 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_capenable & IFCAP_POLLING) { - if (sc->rxcycles <= 0) - break; - sc->rxcycles--; - } -#endif - - cur_rx = &sc->vge_ldata.vge_rx_list[i]; - m = sc->vge_ldata.vge_rx_mbuf[i]; - total_len = VGE_RXBYTES(cur_rx); + prod = sc->vge_cdata.vge_rx_prodidx; + for (prog = 0; count > 0 && + (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0; + VGE_RX_DESC_INC(prod)) { + cur_rx = &sc->vge_rdata.vge_rx_ring[prod]; rxstat = le32toh(cur_rx->vge_sts); + if ((rxstat & VGE_RDSTS_OWN) != 0) + break; + count--; + prog++; 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]); + total_len = VGE_RXBYTES(rxstat); + rxd = &sc->vge_cdata.vge_rxdesc[prod]; + m = rxd->rx_m; /* * If the 'start of frame' bit is set, this indicates @@ -1369,17 +1454,22 @@ vge_rxeof(sc) * 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; + if ((rxstat & VGE_RXPKT_SOF) != 0) { + if (vge_newbuf(sc, prod) != 0) { + ifp->if_iqdrops++; + VGE_CHAIN_RESET(sc); + vge_discard_rxbuf(sc, prod); + continue; + } + m->m_len = MCLBYTES - VGE_RX_BUF_ALIGN; + if (sc->vge_cdata.vge_head == NULL) { + sc->vge_cdata.vge_head = m; + sc->vge_cdata.vge_tail = m; + } else { + m->m_flags &= ~M_PKTHDR; + sc->vge_cdata.vge_tail->m_next = m; + sc->vge_cdata.vge_tail = m; } - vge_newbuf(sc, i, NULL); - VGE_RX_DESC_INC(i); continue; } @@ -1391,43 +1481,32 @@ vge_rxeof(sc) * 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. + * We also want to receive bad-checksummed frames and + * and frames with bad-length. */ - if (!(rxstat & VGE_RDSTS_RXOK) && !(rxstat & VGE_RDSTS_VIDM) - && !(rxstat & VGE_RDSTS_CSUMERR)) { + if ((rxstat & VGE_RDSTS_RXOK) == 0 && + (rxstat & (VGE_RDSTS_VIDM | VGE_RDSTS_RLERR | + VGE_RDSTS_CSUMERR)) == 0) { 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); + VGE_CHAIN_RESET(sc); + vge_discard_rxbuf(sc, prod); 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); + if (vge_newbuf(sc, prod) != 0) { + ifp->if_iqdrops++; + VGE_CHAIN_RESET(sc); + vge_discard_rxbuf(sc, prod); continue; } - VGE_RX_DESC_INC(i); - - if (sc->vge_head != NULL) { - m->m_len = total_len % (MCLBYTES - VGE_ETHER_ALIGN); + /* Chain received mbufs. */ + if (sc->vge_cdata.vge_head != NULL) { + m->m_len = total_len % (MCLBYTES - VGE_RX_BUF_ALIGN); /* * Special case: if there's 4 bytes or less * in this buffer, the mbuf can be discarded: @@ -1435,46 +1514,47 @@ vge_rxeof(sc) * care about anyway. */ if (m->m_len <= ETHER_CRC_LEN) { - sc->vge_tail->m_len -= + sc->vge_cdata.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; + sc->vge_cdata.vge_tail->m_next = m; } - m = sc->vge_head; - sc->vge_head = sc->vge_tail = NULL; + m = sc->vge_cdata.vge_head; + m->m_flags |= M_PKTHDR; m->m_pkthdr.len = total_len - ETHER_CRC_LEN; - } else + } else { + m->m_flags |= M_PKTHDR; m->m_pkthdr.len = m->m_len = (total_len - ETHER_CRC_LEN); + } -#ifdef VGE_FIXUP_RX +#ifndef __NO_STRICT_ALIGNMENT vge_fixup_rx(m); #endif - ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; /* Do RX checksumming if enabled */ - if (ifp->if_capenable & IFCAP_RXCSUM) { - + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0 && + (rxctl & VGE_RDCTL_FRAG) == 0) { /* Check IP header checksum */ - if (rxctl & VGE_RDCTL_IPPKT) + if ((rxctl & VGE_RDCTL_IPPKT) != 0) m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; - if (rxctl & VGE_RDCTL_IPCSUMOK) + if ((rxctl & VGE_RDCTL_IPCSUMOK) != 0) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; /* Check TCP/UDP checksum */ - if (rxctl & (VGE_RDCTL_TCPPKT|VGE_RDCTL_UDPPKT) && + if (rxctl & (VGE_RDCTL_TCPPKT | VGE_RDCTL_UDPPKT) && rxctl & VGE_RDCTL_PROTOCSUMOK) { m->m_pkthdr.csum_flags |= - CSUM_DATA_VALID|CSUM_PSEUDO_HDR; + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } - if (rxstat & VGE_RDSTS_VTAG) { + if ((rxstat & VGE_RDSTS_VTAG) != 0) { /* * The 32-bit rxctl register is stored in little-endian. * However, the 16-bit vlan tag is stored in big-endian, @@ -1488,120 +1568,107 @@ vge_rxeof(sc) VGE_UNLOCK(sc); (*ifp->if_input)(ifp, m); VGE_LOCK(sc); - - lim++; - if (lim == VGE_RX_DESC_CNT) - break; - + sc->vge_cdata.vge_head = NULL; + sc->vge_cdata.vge_tail = NULL; } - /* 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 (lim); + if (prog > 0) { + sc->vge_cdata.vge_rx_prodidx = prod; + bus_dmamap_sync(sc->vge_cdata.vge_rx_ring_tag, + sc->vge_cdata.vge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + /* Update residue counter. */ + if (sc->vge_cdata.vge_rx_commit != 0) { + CSR_WRITE_2(sc, VGE_RXDESC_RESIDUECNT, + sc->vge_cdata.vge_rx_commit); + sc->vge_cdata.vge_rx_commit = 0; + } + } + return (prog); } static void -vge_txeof(sc) - struct vge_softc *sc; +vge_txeof(struct vge_softc *sc) { - struct ifnet *ifp; - u_int32_t txstat; - int idx; + struct ifnet *ifp; + struct vge_tx_desc *cur_tx; + struct vge_txdesc *txd; + uint32_t txstat; + int cons, prod; + + VGE_LOCK_ASSERT(sc); ifp = sc->vge_ifp; - idx = sc->vge_ldata.vge_tx_considx; - /* Invalidate the TX descriptor list */ + if (sc->vge_cdata.vge_tx_cnt == 0) + return; - 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_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_timer = 0; - } + bus_dmamap_sync(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); /* - * 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. + * Go through our tx list and free mbufs for those + * frames that have been transmitted. */ - if (sc->vge_ldata.vge_tx_free != VGE_TX_DESC_CNT) { - CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_TIMER0_ENABLE); - } + cons = sc->vge_cdata.vge_tx_considx; + prod = sc->vge_cdata.vge_tx_prodidx; + for (; cons != prod; VGE_TX_DESC_INC(cons)) { + cur_tx = &sc->vge_rdata.vge_tx_ring[cons]; + txstat = le32toh(cur_tx->vge_sts); + if ((txstat & VGE_TDSTS_OWN) != 0) + break; + sc->vge_cdata.vge_tx_cnt--; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - return; + txd = &sc->vge_cdata.vge_txdesc[cons]; + bus_dmamap_sync(sc->vge_cdata.vge_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->vge_cdata.vge_tx_tag, txd->tx_dmamap); + + KASSERT(txd->tx_m != NULL, ("%s: freeing NULL mbuf!\n", + __func__)); + m_freem(txd->tx_m); + txd->tx_m = NULL; + txd->tx_desc->vge_frag[0].vge_addrhi = 0; + } + bus_dmamap_sync(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + sc->vge_cdata.vge_tx_considx = cons; + if (sc->vge_cdata.vge_tx_cnt == 0) + sc->vge_timer = 0; } static void -vge_tick(xsc) - void *xsc; +vge_link_statchg(void *xsc) { - struct vge_softc *sc; - struct ifnet *ifp; - struct mii_data *mii; + struct vge_softc *sc; + struct ifnet *ifp; + struct mii_data *mii; sc = xsc; ifp = sc->vge_ifp; - VGE_LOCK(sc); + VGE_LOCK_ASSERT(sc); mii = device_get_softc(sc->vge_miibus); - mii_tick(mii); - if (sc->vge_link) { + mii_pollstat(mii); + if ((sc->vge_flags & VGE_FLAG_LINK) != 0) { if (!(mii->mii_media_status & IFM_ACTIVE)) { - sc->vge_link = 0; + sc->vge_flags &= ~VGE_FLAG_LINK; if_link_state_change(sc->vge_ifp, LINK_STATE_DOWN); } } else { if (mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - sc->vge_link = 1; + sc->vge_flags |= VGE_FLAG_LINK; if_link_state_change(sc->vge_ifp, LINK_STATE_UP); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - taskqueue_enqueue(taskqueue_swi, - &sc->vge_txtask); + vge_start_locked(ifp); } } - - VGE_UNLOCK(sc); - - return; } #ifdef DEVICE_POLLING @@ -1615,15 +1682,14 @@ vge_poll (struct ifnet *ifp, enum poll_cmd cmd, int count) if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) goto done; - sc->rxcycles = count; - rx_npkts = vge_rxeof(sc); + rx_npkts = vge_rxeof(sc, count); vge_txeof(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - taskqueue_enqueue(taskqueue_swi, &sc->vge_txtask); + vge_start_locked(ifp); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ - u_int32_t status; + uint32_t status; status = CSR_READ_4(sc, VGE_ISR); if (status == 0xFFFFFFFF) goto done; @@ -1635,12 +1701,13 @@ vge_poll (struct ifnet *ifp, enum poll_cmd cmd, int count) */ if (status & VGE_ISR_TXDMA_STALL || - status & VGE_ISR_RXDMA_STALL) - vge_init(sc); + status & VGE_ISR_RXDMA_STALL) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vge_init_locked(sc); + } if (status & (VGE_ISR_RXOFLOW|VGE_ISR_RXNODESC)) { - vge_rxeof(sc); - ifp->if_ierrors++; + vge_rxeof(sc, count); CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_RUN); CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_WAK); } @@ -1652,23 +1719,18 @@ done: #endif /* DEVICE_POLLING */ static void -vge_intr(arg) - void *arg; +vge_intr(void *arg) { - struct vge_softc *sc; - struct ifnet *ifp; - u_int32_t status; + struct vge_softc *sc; + struct ifnet *ifp; + uint32_t status; sc = arg; - - if (sc->suspended) { - return; - } - VGE_LOCK(sc); - ifp = sc->vge_ifp; - if (!(ifp->if_flags & IFF_UP)) { + ifp = sc->vge_ifp; + if ((sc->vge_flags & VGE_FLAG_SUSPENDED) != 0 || + (ifp->if_flags & IFF_UP) == 0) { VGE_UNLOCK(sc); return; } @@ -1682,189 +1744,223 @@ vge_intr(arg) /* 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; - + status = CSR_READ_4(sc, VGE_ISR); + CSR_WRITE_4(sc, VGE_ISR, status | VGE_ISR_HOLDOFF_RELOAD); + /* If the card has gone away the read returns 0xffff. */ + if (status == 0xFFFFFFFF || (status & VGE_INTRS) == 0) + goto done; + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if (status & (VGE_ISR_RXOK|VGE_ISR_RXOK_HIPRIO)) - vge_rxeof(sc); - + vge_rxeof(sc, VGE_RX_DESC_CNT); if (status & (VGE_ISR_RXOFLOW|VGE_ISR_RXNODESC)) { - vge_rxeof(sc); + vge_rxeof(sc, VGE_RX_DESC_CNT); 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)) + if (status & (VGE_ISR_TXOK0|VGE_ISR_TXOK_HIPRIO)) vge_txeof(sc); - if (status & (VGE_ISR_TXDMA_STALL|VGE_ISR_RXDMA_STALL)) - vge_init(sc); + if (status & (VGE_ISR_TXDMA_STALL|VGE_ISR_RXDMA_STALL)) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vge_init_locked(sc); + } if (status & VGE_ISR_LINKSTS) - vge_tick(sc); + vge_link_statchg(sc); } +done: + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + /* Re-enable interrupts */ + CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK); - /* Re-enable interrupts */ - CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK); - + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vge_start_locked(ifp); + } VGE_UNLOCK(sc); - - if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - 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; +vge_encap(struct vge_softc *sc, struct mbuf **m_head) { - struct mbuf *m_new = NULL; - struct vge_dmaload_arg arg; - bus_dmamap_t map; - int error; + struct vge_txdesc *txd; + struct vge_tx_frag *frag; + struct mbuf *m; + bus_dma_segment_t txsegs[VGE_MAXTXSEGS]; + int error, i, nsegs, padlen; + uint32_t cflags; - if (sc->vge_ldata.vge_tx_free <= 2) - return (EFBIG); + VGE_LOCK_ASSERT(sc); - arg.vge_flags = 0; + M_ASSERTPKTHDR((*m_head)); - 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); + /* Argh. This chip does not autopad short frames. */ + if ((*m_head)->m_pkthdr.len < VGE_MIN_FRAMELEN) { + m = *m_head; + padlen = VGE_MIN_FRAMELEN - m->m_pkthdr.len; + if (M_WRITABLE(m) == 0) { + /* Get a writable copy. */ + m = m_dup(*m_head, M_DONTWAIT); + m_freem(*m_head); + if (m == NULL) { + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m; } + if (M_TRAILINGSPACE(m) < padlen) { + m = m_defrag(m, M_DONTWAIT); + if (m == NULL) { + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + } + /* + * Manually pad short frames, and zero the pad space + * to avoid leaking data. + */ + bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); + m->m_pkthdr.len += padlen; + m->m_len = m->m_pkthdr.len; + *m_head = m; } - sc->vge_ldata.vge_tx_mbuf[idx] = m_head; - sc->vge_ldata.vge_tx_free--; + txd = &sc->vge_cdata.vge_txdesc[sc->vge_cdata.vge_tx_prodidx]; + + error = bus_dmamap_load_mbuf_sg(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap, *m_head, txsegs, &nsegs, 0); + if (error == EFBIG) { + m = m_collapse(*m_head, M_DONTWAIT, VGE_MAXTXSEGS); + if (m == NULL) { + m_freem(*m_head); + *m_head = NULL; + return (ENOMEM); + } + *m_head = m; + error = bus_dmamap_load_mbuf_sg(sc->vge_cdata.vge_tx_tag, + txd->tx_dmamap, *m_head, txsegs, &nsegs, 0); + if (error != 0) { + m_freem(*m_head); + *m_head = NULL; + return (error); + } + } else if (error != 0) + return (error); + bus_dmamap_sync(sc->vge_cdata.vge_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_PREWRITE); + + m = *m_head; + cflags = 0; + + /* Configure checksum offload. */ + if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) + cflags |= VGE_TDCTL_IPCSUM; + if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0) + cflags |= VGE_TDCTL_TCPCSUM; + if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0) + cflags |= VGE_TDCTL_UDPCSUM; + + /* Configure VLAN. */ + if ((m->m_flags & M_VLANTAG) != 0) + cflags |= m->m_pkthdr.ether_vtag | VGE_TDCTL_VTAG; + txd->tx_desc->vge_sts = htole32(m->m_pkthdr.len << 16); + /* + * XXX + * Velocity family seems to support TSO but no information + * for MSS configuration is available. Also the number of + * fragments supported by a descriptor is too small to hold + * entire 64KB TCP/IP segment. Maybe VGE_TD_LS_MOF, + * VGE_TD_LS_SOF and VGE_TD_LS_EOF could be used to build + * longer chain of buffers but no additional information is + * available. + * + * When telling the chip how many segments there are, we + * must use nsegs + 1 instead of just nsegs. Darned if I + * know why. This also means we can't use the last fragment + * field of Tx descriptor. + */ + txd->tx_desc->vge_ctl = htole32(cflags | ((nsegs + 1) << 28) | + VGE_TD_LS_NORM); + for (i = 0; i < nsegs; i++) { + frag = &txd->tx_desc->vge_frag[i]; + frag->vge_addrlo = htole32(VGE_ADDR_LO(txsegs[i].ds_addr)); + frag->vge_addrhi = htole32(VGE_ADDR_HI(txsegs[i].ds_addr) | + (VGE_BUFLEN(txsegs[i].ds_len) << 16)); + } + + sc->vge_cdata.vge_tx_cnt++; + VGE_TX_DESC_INC(sc->vge_cdata.vge_tx_prodidx); /* - * Set up hardware VLAN tagging. + * Finally request interrupt and give the first descriptor + * ownership to hardware. */ - - if (m_head->m_flags & M_VLANTAG) - sc->vge_ldata.vge_tx_list[idx].vge_ctl |= - htole32(m_head->m_pkthdr.ether_vtag | VGE_TDCTL_VTAG); - - sc->vge_ldata.vge_tx_list[idx].vge_sts |= htole32(VGE_TDSTS_OWN); + txd->tx_desc->vge_ctl |= htole32(VGE_TDCTL_TIC); + txd->tx_desc->vge_sts |= htole32(VGE_TDSTS_OWN); + txd->tx_m = m; 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; +vge_start(struct ifnet *ifp) { - struct vge_softc *sc; - struct mbuf *m_head = NULL; - int idx, pidx = 0; + struct vge_softc *sc; sc = ifp->if_softc; VGE_LOCK(sc); + vge_start_locked(ifp); + VGE_UNLOCK(sc); +} - if (!sc->vge_link || ifp->if_drv_flags & IFF_DRV_OACTIVE) { - VGE_UNLOCK(sc); + +static void +vge_start_locked(struct ifnet *ifp) +{ + struct vge_softc *sc; + struct vge_txdesc *txd; + struct mbuf *m_head; + int enq, idx; + + sc = ifp->if_softc; + + VGE_LOCK_ASSERT(sc); + + if ((sc->vge_flags & VGE_FLAG_LINK) == 0 || + (ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) return; - } - if (IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { - 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) { + idx = sc->vge_cdata.vge_tx_prodidx; + VGE_TX_DESC_DEC(idx); + for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && + sc->vge_cdata.vge_tx_cnt < VGE_TX_DESC_CNT - 1; ) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; - - if (vge_encap(sc, m_head, idx)) { + /* + * Pack the data into the transmit ring. If we + * don't have room, set the OACTIVE flag and wait + * for the NIC to drain the ring. + */ + if (vge_encap(sc, &m_head)) { + if (m_head == NULL) + break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } - sc->vge_ldata.vge_tx_list[pidx].vge_frag[0].vge_buflen |= - htole16(VGE_TXDESC_Q); - - pidx = idx; + txd = &sc->vge_cdata.vge_txdesc[idx]; + txd->tx_desc->vge_frag[0].vge_addrhi |= htole32(VGE_TXDESC_Q); VGE_TX_DESC_INC(idx); + enq++; /* * If there's a BPF listener, bounce a copy of this frame * to him. @@ -1872,55 +1968,42 @@ vge_start(ifp) ETHER_BPF_MTAP(ifp, m_head); } - if (idx == sc->vge_ldata.vge_tx_prodidx) { - VGE_UNLOCK(sc); - return; + if (enq > 0) { + bus_dmamap_sync(sc->vge_cdata.vge_tx_ring_tag, + sc->vge_cdata.vge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + /* Issue a transmit command. */ + CSR_WRITE_2(sc, VGE_TXQCSRS, VGE_TXQCSR_WAK0); + /* + * Set a timeout in case the chip goes out to lunch. + */ + sc->vge_timer = 5; } - - /* 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; +vge_init(void *xsc) { - struct vge_softc *sc = xsc; - struct ifnet *ifp = sc->vge_ifp; - struct mii_data *mii; - int i; + struct vge_softc *sc = xsc; VGE_LOCK(sc); + vge_init_locked(sc); + VGE_UNLOCK(sc); +} + +static void +vge_init_locked(struct vge_softc *sc) +{ + struct ifnet *ifp = sc->vge_ifp; + struct mii_data *mii; + int error, i; + + VGE_LOCK_ASSERT(sc); mii = device_get_softc(sc->vge_miibus); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + /* * Cancel pending I/O and free all RX/TX buffers. */ @@ -1931,9 +2014,14 @@ vge_init(xsc) * Initialize the RX and TX descriptors and mbufs. */ - vge_rx_list_init(sc); + error = vge_rx_list_init(sc); + if (error != 0) { + device_printf(sc->vge_dev, "no memory for Rx buffers.\n"); + return; + } vge_tx_list_init(sc); - + /* Clear MAC statistics. */ + vge_stats_clear(sc); /* Set our station address */ for (i = 0; i < ETHER_ADDR_LEN; i++) CSR_WRITE_1(sc, VGE_PAR0 + i, IF_LLADDR(sc->vge_ifp)[i]); @@ -1943,7 +2031,7 @@ vge_init(xsc) * 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); + CSR_SETBIT_1(sc, VGE_RXCFG, VGE_RXFIFOTHR_128BYTES); /* Set DMA burst length */ CSR_CLRBIT_1(sc, VGE_DMACFG0, VGE_DMACFG0_BURSTLEN); @@ -1964,15 +2052,20 @@ vge_init(xsc) * Note that we only use one transmit queue. */ + CSR_WRITE_4(sc, VGE_TXDESC_HIADDR, + VGE_ADDR_HI(sc->vge_rdata.vge_tx_ring_paddr)); CSR_WRITE_4(sc, VGE_TXDESC_ADDR_LO0, - VGE_ADDR_LO(sc->vge_ldata.vge_tx_list_addr)); + VGE_ADDR_LO(sc->vge_rdata.vge_tx_ring_paddr)); 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)); + VGE_ADDR_LO(sc->vge_rdata.vge_rx_ring_paddr)); CSR_WRITE_2(sc, VGE_RXDESCNUM, VGE_RX_DESC_CNT - 1); CSR_WRITE_2(sc, VGE_RXDESC_RESIDUECNT, VGE_RX_DESC_CNT); + /* Configure interrupt moderation. */ + vge_intr_holdoff(sc); + /* 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); @@ -1980,29 +2073,12 @@ vge_init(xsc) /* 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); + /* Set up receiver filter. */ + vge_rxfilter(sc); + vge_setvlan(sc); /* Enable flow control */ @@ -2016,42 +2092,6 @@ vge_init(xsc) 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. @@ -2066,70 +2106,66 @@ vge_init(xsc) * Enable interrupts. */ CSR_WRITE_4(sc, VGE_IMR, VGE_INTRS); - CSR_WRITE_4(sc, VGE_ISR, 0); + CSR_WRITE_4(sc, VGE_ISR, 0xFFFFFFFF); CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK); } + sc->vge_flags &= ~VGE_FLAG_LINK; mii_mediachg(mii); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - - sc->vge_if_flags = 0; - sc->vge_link = 0; - - VGE_UNLOCK(sc); - - return; + callout_reset(&sc->vge_watchdog, hz, vge_watchdog, sc); } /* * Set media options. */ static int -vge_ifmedia_upd(ifp) - struct ifnet *ifp; +vge_ifmedia_upd(struct ifnet *ifp) { - struct vge_softc *sc; - struct mii_data *mii; + struct vge_softc *sc; + struct mii_data *mii; + int error; sc = ifp->if_softc; VGE_LOCK(sc); mii = device_get_softc(sc->vge_miibus); - mii_mediachg(mii); + error = mii_mediachg(mii); VGE_UNLOCK(sc); - return (0); + return (error); } /* * Report current media status. */ static void -vge_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; +vge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { - struct vge_softc *sc; - struct mii_data *mii; + struct vge_softc *sc; + struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->vge_miibus); + VGE_LOCK(sc); + if ((ifp->if_flags & IFF_UP) == 0) { + VGE_UNLOCK(sc); + return; + } mii_pollstat(mii); + VGE_UNLOCK(sc); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; - - return; } static void -vge_miibus_statchg(dev) - device_t dev; +vge_miibus_statchg(device_t dev) { - struct vge_softc *sc; - struct mii_data *mii; - struct ifmedia_entry *ife; + struct vge_softc *sc; + struct mii_data *mii; + struct ifmedia_entry *ife; sc = device_get_softc(dev); mii = device_get_softc(sc->vge_miibus); @@ -2169,52 +2205,50 @@ vge_miibus_statchg(dev) IFM_SUBTYPE(ife->ifm_media)); break; } - - return; } static int -vge_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; +vge_ioctl(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; + struct vge_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0, mask; switch (command) { case SIOCSIFMTU: - if (ifr->ifr_mtu > VGE_JUMBO_MTU) + VGE_LOCK(sc); + if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > VGE_JUMBO_MTU) error = EINVAL; - ifp->if_mtu = ifr->ifr_mtu; + else if (ifp->if_mtu != ifr->ifr_mtu) { + if (ifr->ifr_mtu > ETHERMTU && + (sc->vge_flags & VGE_FLAG_JUMBO) == 0) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + } + VGE_UNLOCK(sc); break; case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { - if (ifp->if_drv_flags & IFF_DRV_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_drv_flags & IFF_DRV_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_drv_flags & IFF_DRV_RUNNING) - vge_stop(sc); - } + VGE_LOCK(sc); + if ((ifp->if_flags & IFF_UP) != 0) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && + ((ifp->if_flags ^ sc->vge_if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) != 0) + vge_rxfilter(sc); + else + vge_init_locked(sc); + } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + vge_stop(sc); sc->vge_if_flags = ifp->if_flags; + VGE_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: - vge_setmulti(sc); + VGE_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + vge_rxfilter(sc); + VGE_UNLOCK(sc); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: @@ -2222,14 +2256,13 @@ vge_ioctl(ifp, command, data) error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: - { - int mask = ifr->ifr_reqcap ^ ifp->if_capenable; + mask = ifr->ifr_reqcap ^ ifp->if_capenable; #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { error = ether_poll_register(vge_poll, ifp); if (error) - return(error); + return (error); VGE_LOCK(sc); /* Disable interrupts */ CSR_WRITE_4(sc, VGE_IMR, 0); @@ -2248,6 +2281,7 @@ vge_ioctl(ifp, command, data) } } #endif /* DEVICE_POLLING */ + VGE_LOCK(sc); if ((mask & IFCAP_TXCSUM) != 0 && (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { ifp->if_capenable ^= IFCAP_TXCSUM; @@ -2259,7 +2293,25 @@ vge_ioctl(ifp, command, data) if ((mask & IFCAP_RXCSUM) != 0 && (ifp->if_capabilities & IFCAP_RXCSUM) != 0) ifp->if_capenable ^= IFCAP_RXCSUM; - } + if ((mask & IFCAP_WOL_UCAST) != 0 && + (ifp->if_capabilities & IFCAP_WOL_UCAST) != 0) + ifp->if_capenable ^= IFCAP_WOL_UCAST; + if ((mask & IFCAP_WOL_MCAST) != 0 && + (ifp->if_capabilities & IFCAP_WOL_MCAST) != 0) + ifp->if_capenable ^= IFCAP_WOL_MCAST; + if ((mask & IFCAP_WOL_MAGIC) != 0 && + (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) + ifp->if_capenable ^= IFCAP_WOL_MAGIC; + if ((mask & IFCAP_VLAN_HWCSUM) != 0 && + (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) + ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; + if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && + (IFCAP_VLAN_HWTAGGING & ifp->if_capabilities) != 0) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + vge_setvlan(sc); + } + VGE_UNLOCK(sc); + VLAN_CAPABILITIES(ifp); break; default: error = ether_ioctl(ifp, command, data); @@ -2270,24 +2322,27 @@ vge_ioctl(ifp, command, data) } static void -vge_watchdog(ifp) - struct ifnet *ifp; +vge_watchdog(void *arg) { - struct vge_softc *sc; + struct vge_softc *sc; + struct ifnet *ifp; - sc = ifp->if_softc; - VGE_LOCK(sc); - printf("vge%d: watchdog timeout\n", sc->vge_unit); + sc = arg; + VGE_LOCK_ASSERT(sc); + vge_stats_update(sc); + callout_reset(&sc->vge_watchdog, hz, vge_watchdog, sc); + if (sc->vge_timer == 0 || --sc->vge_timer > 0) + return; + + ifp = sc->vge_ifp; + if_printf(ifp, "watchdog timeout\n"); ifp->if_oerrors++; vge_txeof(sc); - vge_rxeof(sc); + vge_rxeof(sc, VGE_RX_DESC_CNT); - vge_init(sc); - - VGE_UNLOCK(sc); - - return; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vge_init_locked(sc); } /* @@ -2295,15 +2350,14 @@ vge_watchdog(ifp) * RX and TX lists. */ static void -vge_stop(sc) - struct vge_softc *sc; +vge_stop(struct vge_softc *sc) { - register int i; - struct ifnet *ifp; + struct ifnet *ifp; - VGE_LOCK(sc); + VGE_LOCK_ASSERT(sc); ifp = sc->vge_ifp; - ifp->if_timer = 0; + sc->vge_timer = 0; + callout_stop(&sc->vge_watchdog); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); @@ -2314,36 +2368,10 @@ vge_stop(sc) 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; + vge_stats_update(sc); + VGE_CHAIN_RESET(sc); + vge_txeof(sc); + vge_freebufs(sc); } /* @@ -2352,16 +2380,17 @@ vge_stop(sc) * resume. */ static int -vge_suspend(dev) - device_t dev; +vge_suspend(device_t dev) { - struct vge_softc *sc; + struct vge_softc *sc; sc = device_get_softc(dev); + VGE_LOCK(sc); vge_stop(sc); - - sc->suspended = 1; + vge_setwol(sc); + sc->vge_flags |= VGE_FLAG_SUSPENDED; + VGE_UNLOCK(sc); return (0); } @@ -2372,24 +2401,35 @@ vge_suspend(dev) * appropriate. */ static int -vge_resume(dev) - device_t dev; +vge_resume(device_t dev) { - struct vge_softc *sc; - struct ifnet *ifp; + struct vge_softc *sc; + struct ifnet *ifp; + uint16_t pmstat; sc = device_get_softc(dev); + VGE_LOCK(sc); + if ((sc->vge_flags & VGE_FLAG_PMCAP) != 0) { + /* Disable PME and clear PME status. */ + pmstat = pci_read_config(sc->vge_dev, + sc->vge_pmcap + PCIR_POWER_STATUS, 2); + if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) { + pmstat &= ~PCIM_PSTAT_PMEENABLE; + pci_write_config(sc->vge_dev, + sc->vge_pmcap + PCIR_POWER_STATUS, pmstat, 2); + } + } + vge_clrwol(sc); + /* Restart MII auto-polling. */ + vge_miipoll_start(sc); ifp = sc->vge_ifp; - - /* 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; + /* Reinitialize interface if necessary. */ + if ((ifp->if_flags & IFF_UP) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + vge_init_locked(sc); + } + sc->vge_flags &= ~VGE_FLAG_SUSPENDED; + VGE_UNLOCK(sc); return (0); } @@ -2399,14 +2439,444 @@ vge_resume(dev) * get confused by errant DMAs when rebooting. */ static int -vge_shutdown(dev) - device_t dev; +vge_shutdown(device_t dev) { - struct vge_softc *sc; - sc = device_get_softc(dev); - - vge_stop(sc); - - return (0); + return (vge_suspend(dev)); +} + +#define VGE_SYSCTL_STAT_ADD32(c, h, n, p, d) \ + SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) + +static void +vge_sysctl_node(struct vge_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *child, *parent; + struct sysctl_oid *tree; + struct vge_hw_stats *stats; + + stats = &sc->vge_stats; + ctx = device_get_sysctl_ctx(sc->vge_dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vge_dev)); + + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "int_holdoff", + CTLFLAG_RW, &sc->vge_int_holdoff, 0, "interrupt holdoff"); + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_coal_pkt", + CTLFLAG_RW, &sc->vge_rx_coal_pkt, 0, "rx coalescing packet"); + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_coal_pkt", + CTLFLAG_RW, &sc->vge_tx_coal_pkt, 0, "tx coalescing packet"); + + /* Pull in device tunables. */ + sc->vge_int_holdoff = VGE_INT_HOLDOFF_DEFAULT; + resource_int_value(device_get_name(sc->vge_dev), + device_get_unit(sc->vge_dev), "int_holdoff", &sc->vge_int_holdoff); + sc->vge_rx_coal_pkt = VGE_RX_COAL_PKT_DEFAULT; + resource_int_value(device_get_name(sc->vge_dev), + device_get_unit(sc->vge_dev), "rx_coal_pkt", &sc->vge_rx_coal_pkt); + sc->vge_tx_coal_pkt = VGE_TX_COAL_PKT_DEFAULT; + resource_int_value(device_get_name(sc->vge_dev), + device_get_unit(sc->vge_dev), "tx_coal_pkt", &sc->vge_tx_coal_pkt); + + tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, + NULL, "VGE statistics"); + parent = SYSCTL_CHILDREN(tree); + + /* Rx statistics. */ + tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD, + NULL, "RX MAC statistics"); + child = SYSCTL_CHILDREN(tree); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames", + &stats->rx_frames, "frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", + &stats->rx_good_frames, "Good frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "fifo_oflows", + &stats->rx_fifo_oflows, "FIFO overflows"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "runts", + &stats->rx_runts, "Too short frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "runts_errs", + &stats->rx_runts_errs, "Too short frames with errors"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_64", + &stats->rx_pkts_64, "64 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_65_127", + &stats->rx_pkts_65_127, "65 to 127 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_128_255", + &stats->rx_pkts_128_255, "128 to 255 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_256_511", + &stats->rx_pkts_256_511, "256 to 511 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_512_1023", + &stats->rx_pkts_512_1023, "512 to 1023 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_1024_1518", + &stats->rx_pkts_1024_1518, "1024 to 1518 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_1519_max", + &stats->rx_pkts_1519_max, "1519 to max frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_1519_max_errs", + &stats->rx_pkts_1519_max_errs, "1519 to max frames with error"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_jumbo", + &stats->rx_jumbos, "Jumbo frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "crcerrs", + &stats->rx_crcerrs, "CRC errors"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", + &stats->rx_pause_frames, "CRC errors"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "align_errs", + &stats->rx_alignerrs, "Alignment errors"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "nobufs", + &stats->rx_nobufs, "Frames with no buffer event"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "sym_errs", + &stats->rx_symerrs, "Frames with symbol errors"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "len_errs", + &stats->rx_lenerrs, "Frames with length mismatched"); + + /* Tx statistics. */ + tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, + NULL, "TX MAC statistics"); + child = SYSCTL_CHILDREN(tree); + VGE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", + &stats->tx_good_frames, "Good frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_64", + &stats->tx_pkts_64, "64 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_65_127", + &stats->tx_pkts_65_127, "65 to 127 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_128_255", + &stats->tx_pkts_128_255, "128 to 255 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_256_511", + &stats->tx_pkts_256_511, "256 to 511 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_512_1023", + &stats->tx_pkts_512_1023, "512 to 1023 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_1024_1518", + &stats->tx_pkts_1024_1518, "1024 to 1518 bytes frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "frames_jumbo", + &stats->tx_jumbos, "Jumbo frames"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "colls", + &stats->tx_colls, "Collisions"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "late_colls", + &stats->tx_latecolls, "Late collisions"); + VGE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", + &stats->tx_pause, "Pause frames"); +#ifdef VGE_ENABLE_SQEERR + VGE_SYSCTL_STAT_ADD32(ctx, child, "sqeerrs", + &stats->tx_sqeerrs, "SQE errors"); +#endif + /* Clear MAC statistics. */ + vge_stats_clear(sc); +} + +#undef VGE_SYSCTL_STAT_ADD32 + +static void +vge_stats_clear(struct vge_softc *sc) +{ + int i; + + CSR_WRITE_1(sc, VGE_MIBCSR, + CSR_READ_1(sc, VGE_MIBCSR) | VGE_MIBCSR_FREEZE); + CSR_WRITE_1(sc, VGE_MIBCSR, + CSR_READ_1(sc, VGE_MIBCSR) | VGE_MIBCSR_CLR); + for (i = VGE_TIMEOUT; i > 0; i--) { + DELAY(1); + if ((CSR_READ_1(sc, VGE_MIBCSR) & VGE_MIBCSR_CLR) == 0) + break; + } + if (i == 0) + device_printf(sc->vge_dev, "MIB clear timed out!\n"); + CSR_WRITE_1(sc, VGE_MIBCSR, CSR_READ_1(sc, VGE_MIBCSR) & + ~VGE_MIBCSR_FREEZE); +} + +static void +vge_stats_update(struct vge_softc *sc) +{ + struct vge_hw_stats *stats; + struct ifnet *ifp; + uint32_t mib[VGE_MIB_CNT], val; + int i; + + VGE_LOCK_ASSERT(sc); + + stats = &sc->vge_stats; + ifp = sc->vge_ifp; + + CSR_WRITE_1(sc, VGE_MIBCSR, + CSR_READ_1(sc, VGE_MIBCSR) | VGE_MIBCSR_FLUSH); + for (i = VGE_TIMEOUT; i > 0; i--) { + DELAY(1); + if ((CSR_READ_1(sc, VGE_MIBCSR) & VGE_MIBCSR_FLUSH) == 0) + break; + } + if (i == 0) { + device_printf(sc->vge_dev, "MIB counter dump timed out!\n"); + vge_stats_clear(sc); + return; + } + + bzero(mib, sizeof(mib)); +reset_idx: + /* Set MIB read index to 0. */ + CSR_WRITE_1(sc, VGE_MIBCSR, + CSR_READ_1(sc, VGE_MIBCSR) | VGE_MIBCSR_RINI); + for (i = 0; i < VGE_MIB_CNT; i++) { + val = CSR_READ_4(sc, VGE_MIBDATA); + if (i != VGE_MIB_DATA_IDX(val)) { + /* Reading interrupted. */ + goto reset_idx; + } + mib[i] = val & VGE_MIB_DATA_MASK; + } + + /* Rx stats. */ + stats->rx_frames += mib[VGE_MIB_RX_FRAMES]; + stats->rx_good_frames += mib[VGE_MIB_RX_GOOD_FRAMES]; + stats->rx_fifo_oflows += mib[VGE_MIB_RX_FIFO_OVERRUNS]; + stats->rx_runts += mib[VGE_MIB_RX_RUNTS]; + stats->rx_runts_errs += mib[VGE_MIB_RX_RUNTS_ERRS]; + stats->rx_pkts_64 += mib[VGE_MIB_RX_PKTS_64]; + stats->rx_pkts_65_127 += mib[VGE_MIB_RX_PKTS_65_127]; + stats->rx_pkts_128_255 += mib[VGE_MIB_RX_PKTS_128_255]; + stats->rx_pkts_256_511 += mib[VGE_MIB_RX_PKTS_256_511]; + stats->rx_pkts_512_1023 += mib[VGE_MIB_RX_PKTS_512_1023]; + stats->rx_pkts_1024_1518 += mib[VGE_MIB_RX_PKTS_1024_1518]; + stats->rx_pkts_1519_max += mib[VGE_MIB_RX_PKTS_1519_MAX]; + stats->rx_pkts_1519_max_errs += mib[VGE_MIB_RX_PKTS_1519_MAX_ERRS]; + stats->rx_jumbos += mib[VGE_MIB_RX_JUMBOS]; + stats->rx_crcerrs += mib[VGE_MIB_RX_CRCERRS]; + stats->rx_pause_frames += mib[VGE_MIB_RX_PAUSE]; + stats->rx_alignerrs += mib[VGE_MIB_RX_ALIGNERRS]; + stats->rx_nobufs += mib[VGE_MIB_RX_NOBUFS]; + stats->rx_symerrs += mib[VGE_MIB_RX_SYMERRS]; + stats->rx_lenerrs += mib[VGE_MIB_RX_LENERRS]; + + /* Tx stats. */ + stats->tx_good_frames += mib[VGE_MIB_TX_GOOD_FRAMES]; + stats->tx_pkts_64 += mib[VGE_MIB_TX_PKTS_64]; + stats->tx_pkts_65_127 += mib[VGE_MIB_TX_PKTS_65_127]; + stats->tx_pkts_128_255 += mib[VGE_MIB_TX_PKTS_128_255]; + stats->tx_pkts_256_511 += mib[VGE_MIB_TX_PKTS_256_511]; + stats->tx_pkts_512_1023 += mib[VGE_MIB_TX_PKTS_512_1023]; + stats->tx_pkts_1024_1518 += mib[VGE_MIB_TX_PKTS_1024_1518]; + stats->tx_jumbos += mib[VGE_MIB_TX_JUMBOS]; + stats->tx_colls += mib[VGE_MIB_TX_COLLS]; + stats->tx_pause += mib[VGE_MIB_TX_PAUSE]; +#ifdef VGE_ENABLE_SQEERR + stats->tx_sqeerrs += mib[VGE_MIB_TX_SQEERRS]; +#endif + stats->tx_latecolls += mib[VGE_MIB_TX_LATECOLLS]; + + /* Update counters in ifnet. */ + ifp->if_opackets += mib[VGE_MIB_TX_GOOD_FRAMES]; + + ifp->if_collisions += mib[VGE_MIB_TX_COLLS] + + mib[VGE_MIB_TX_LATECOLLS]; + + ifp->if_oerrors += mib[VGE_MIB_TX_COLLS] + + mib[VGE_MIB_TX_LATECOLLS]; + + ifp->if_ipackets += mib[VGE_MIB_RX_GOOD_FRAMES]; + + ifp->if_ierrors += mib[VGE_MIB_RX_FIFO_OVERRUNS] + + mib[VGE_MIB_RX_RUNTS] + + mib[VGE_MIB_RX_RUNTS_ERRS] + + mib[VGE_MIB_RX_CRCERRS] + + mib[VGE_MIB_RX_ALIGNERRS] + + mib[VGE_MIB_RX_NOBUFS] + + mib[VGE_MIB_RX_SYMERRS] + + mib[VGE_MIB_RX_LENERRS]; +} + +static void +vge_intr_holdoff(struct vge_softc *sc) +{ + uint8_t intctl; + + VGE_LOCK_ASSERT(sc); + + /* + * Set Tx interrupt supression threshold. + * It's possible to use single-shot timer in VGE_CRS1 register + * in Tx path such that driver can remove most of Tx completion + * interrupts. However this requires additional access to + * VGE_CRS1 register to reload the timer in addintion to + * activating Tx kick command. Another downside is we don't know + * what single-shot timer value should be used in advance so + * reclaiming transmitted mbufs could be delayed a lot which in + * turn slows down Tx operation. + */ + CSR_WRITE_1(sc, VGE_CAMCTL, VGE_PAGESEL_TXSUPPTHR); + CSR_WRITE_1(sc, VGE_TXSUPPTHR, sc->vge_tx_coal_pkt); + + /* Set Rx interrupt suppresion threshold. */ + CSR_WRITE_1(sc, VGE_CAMCTL, VGE_PAGESEL_RXSUPPTHR); + CSR_WRITE_1(sc, VGE_RXSUPPTHR, sc->vge_rx_coal_pkt); + + intctl = CSR_READ_1(sc, VGE_INTCTL1); + intctl &= ~VGE_INTCTL_SC_RELOAD; + intctl |= VGE_INTCTL_HC_RELOAD; + if (sc->vge_tx_coal_pkt <= 0) + intctl |= VGE_INTCTL_TXINTSUP_DISABLE; + else + intctl &= ~VGE_INTCTL_TXINTSUP_DISABLE; + if (sc->vge_rx_coal_pkt <= 0) + intctl |= VGE_INTCTL_RXINTSUP_DISABLE; + else + intctl &= ~VGE_INTCTL_RXINTSUP_DISABLE; + CSR_WRITE_1(sc, VGE_INTCTL1, intctl); + CSR_WRITE_1(sc, VGE_CRC3, VGE_CR3_INT_HOLDOFF); + if (sc->vge_int_holdoff > 0) { + /* Set interrupt holdoff timer. */ + CSR_WRITE_1(sc, VGE_CAMCTL, VGE_PAGESEL_INTHLDOFF); + CSR_WRITE_1(sc, VGE_INTHOLDOFF, + VGE_INT_HOLDOFF_USEC(sc->vge_int_holdoff)); + /* Enable holdoff timer. */ + CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_HOLDOFF); + } +} + +static void +vge_setlinkspeed(struct vge_softc *sc) +{ + struct mii_data *mii; + int aneg, i; + + VGE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->vge_miibus); + mii_pollstat(mii); + aneg = 0; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch IFM_SUBTYPE(mii->mii_media_active) { + case IFM_10_T: + case IFM_100_TX: + return; + case IFM_1000_T: + aneg++; + default: + break; + } + } + vge_miibus_writereg(sc->vge_dev, sc->vge_phyaddr, MII_100T2CR, 0); + vge_miibus_writereg(sc->vge_dev, sc->vge_phyaddr, MII_ANAR, + ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA); + vge_miibus_writereg(sc->vge_dev, sc->vge_phyaddr, MII_BMCR, + BMCR_AUTOEN | BMCR_STARTNEG); + DELAY(1000); + if (aneg != 0) { + /* Poll link state until vge(4) get a 10/100 link. */ + for (i = 0; i < MII_ANEGTICKS_GIGE; i++) { + mii_pollstat(mii); + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) + == (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + return; + default: + break; + } + } + VGE_UNLOCK(sc); + pause("vgelnk", hz); + VGE_LOCK(sc); + } + if (i == MII_ANEGTICKS_GIGE) + device_printf(sc->vge_dev, "establishing link failed, " + "WOL may not work!"); + } + /* + * No link, force MAC to have 100Mbps, full-duplex link. + * This is the last resort and may/may not work. + */ + mii->mii_media_status = IFM_AVALID | IFM_ACTIVE; + mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX; +} + +static void +vge_setwol(struct vge_softc *sc) +{ + struct ifnet *ifp; + uint16_t pmstat; + uint8_t val; + + VGE_LOCK_ASSERT(sc); + + if ((sc->vge_flags & VGE_FLAG_PMCAP) == 0) { + /* No PME capability, PHY power down. */ + vge_miibus_writereg(sc->vge_dev, sc->vge_phyaddr, MII_BMCR, + BMCR_PDOWN); + vge_miipoll_stop(sc); + return; + } + + ifp = sc->vge_ifp; + + /* Clear WOL on pattern match. */ + CSR_WRITE_1(sc, VGE_WOLCR0C, VGE_WOLCR0_PATTERN_ALL); + /* Disable WOL on magic/unicast packet. */ + CSR_WRITE_1(sc, VGE_WOLCR1C, 0x0F); + CSR_WRITE_1(sc, VGE_WOLCFGC, VGE_WOLCFG_SAB | VGE_WOLCFG_SAM | + VGE_WOLCFG_PMEOVR); + if ((ifp->if_capenable & IFCAP_WOL) != 0) { + vge_setlinkspeed(sc); + val = 0; + if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0) + val |= VGE_WOLCR1_UCAST; + if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) + val |= VGE_WOLCR1_MAGIC; + CSR_WRITE_1(sc, VGE_WOLCR1S, val); + val = 0; + if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0) + val |= VGE_WOLCFG_SAM | VGE_WOLCFG_SAB; + CSR_WRITE_1(sc, VGE_WOLCFGS, val | VGE_WOLCFG_PMEOVR); + /* Disable MII auto-polling. */ + vge_miipoll_stop(sc); + } + CSR_SETBIT_1(sc, VGE_DIAGCTL, + VGE_DIAGCTL_MACFORCE | VGE_DIAGCTL_FDXFORCE); + CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_GMII); + + /* Clear WOL status on pattern match. */ + CSR_WRITE_1(sc, VGE_WOLSR0C, 0xFF); + CSR_WRITE_1(sc, VGE_WOLSR1C, 0xFF); + + val = CSR_READ_1(sc, VGE_PWRSTAT); + val |= VGE_STICKHW_SWPTAG; + CSR_WRITE_1(sc, VGE_PWRSTAT, val); + /* Put hardware into sleep. */ + val = CSR_READ_1(sc, VGE_PWRSTAT); + val |= VGE_STICKHW_DS0 | VGE_STICKHW_DS1; + CSR_WRITE_1(sc, VGE_PWRSTAT, val); + /* Request PME if WOL is requested. */ + pmstat = pci_read_config(sc->vge_dev, sc->vge_pmcap + + PCIR_POWER_STATUS, 2); + pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + if ((ifp->if_capenable & IFCAP_WOL) != 0) + pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + pci_write_config(sc->vge_dev, sc->vge_pmcap + PCIR_POWER_STATUS, + pmstat, 2); +} + +static void +vge_clrwol(struct vge_softc *sc) +{ + uint8_t val; + + val = CSR_READ_1(sc, VGE_PWRSTAT); + val &= ~VGE_STICKHW_SWPTAG; + CSR_WRITE_1(sc, VGE_PWRSTAT, val); + /* Disable WOL and clear power state indicator. */ + val = CSR_READ_1(sc, VGE_PWRSTAT); + val &= ~(VGE_STICKHW_DS0 | VGE_STICKHW_DS1); + CSR_WRITE_1(sc, VGE_PWRSTAT, val); + + CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_GMII); + CSR_CLRBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_MACFORCE); + + /* Clear WOL on pattern match. */ + CSR_WRITE_1(sc, VGE_WOLCR0C, VGE_WOLCR0_PATTERN_ALL); + /* Disable WOL on magic/unicast packet. */ + CSR_WRITE_1(sc, VGE_WOLCR1C, 0x0F); + CSR_WRITE_1(sc, VGE_WOLCFGC, VGE_WOLCFG_SAB | VGE_WOLCFG_SAM | + VGE_WOLCFG_PMEOVR); + /* Clear WOL status on pattern match. */ + CSR_WRITE_1(sc, VGE_WOLSR0C, 0xFF); + CSR_WRITE_1(sc, VGE_WOLSR1C, 0xFF); } diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgereg.h b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgereg.h index 8d11a9c3e1..8a53bd89f0 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgereg.h +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgereg.h @@ -29,7 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/sys/dev/vge/if_vgereg.h,v 1.2.22.6.4.1 2010/12/21 17:09:25 kensmith Exp $ */ /* @@ -89,8 +89,8 @@ #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_TXQTIMER 0x3E /* TX queue timer pend register */ +#define VGE_RXQTIMER 0x3F /* RX 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) */ @@ -300,8 +300,7 @@ #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) + VGE_ISR_RXDMA_STALL|VGE_ISR_TXDMA_STALL) /* Interrupt mask register */ @@ -339,19 +338,19 @@ #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_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_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_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 */ +#define VGE_TXQCSR_DEAD3 0x8000 /* queue 3 dead indicator */ /* RX descriptor queue control/status register */ @@ -543,6 +542,90 @@ #define VGE_TXBLOCK_128PKTS 0x08 #define VGE_TXBLOCK_8PKTS 0x0C +/* MIB control/status register */ +#define VGE_MIBCSR_CLR 0x01 +#define VGE_MIBCSR_RINI 0x02 +#define VGE_MIBCSR_FLUSH 0x04 +#define VGE_MIBCSR_FREEZE 0x08 +#define VGE_MIBCSR_HI_80 0x00 +#define VGE_MIBCSR_HI_C0 0x10 +#define VGE_MIBCSR_BISTGO 0x40 +#define VGE_MIBCSR_BISTOK 0x80 + +/* MIB data index. */ +#define VGE_MIB_RX_FRAMES 0 +#define VGE_MIB_RX_GOOD_FRAMES 1 +#define VGE_MIB_TX_GOOD_FRAMES 2 +#define VGE_MIB_RX_FIFO_OVERRUNS 3 +#define VGE_MIB_RX_RUNTS 4 +#define VGE_MIB_RX_RUNTS_ERRS 5 +#define VGE_MIB_RX_PKTS_64 6 +#define VGE_MIB_TX_PKTS_64 7 +#define VGE_MIB_RX_PKTS_65_127 8 +#define VGE_MIB_TX_PKTS_65_127 9 +#define VGE_MIB_RX_PKTS_128_255 10 +#define VGE_MIB_TX_PKTS_128_255 11 +#define VGE_MIB_RX_PKTS_256_511 12 +#define VGE_MIB_TX_PKTS_256_511 13 +#define VGE_MIB_RX_PKTS_512_1023 14 +#define VGE_MIB_TX_PKTS_512_1023 15 +#define VGE_MIB_RX_PKTS_1024_1518 16 +#define VGE_MIB_TX_PKTS_1024_1518 17 +#define VGE_MIB_TX_COLLS 18 +#define VGE_MIB_RX_CRCERRS 19 +#define VGE_MIB_RX_JUMBOS 20 +#define VGE_MIB_TX_JUMBOS 21 +#define VGE_MIB_RX_PAUSE 22 +#define VGE_MIB_TX_PAUSE 23 +#define VGE_MIB_RX_ALIGNERRS 24 +#define VGE_MIB_RX_PKTS_1519_MAX 25 +#define VGE_MIB_RX_PKTS_1519_MAX_ERRS 26 +#define VGE_MIB_TX_SQEERRS 27 +#define VGE_MIB_RX_NOBUFS 28 +#define VGE_MIB_RX_SYMERRS 29 +#define VGE_MIB_RX_LENERRS 30 +#define VGE_MIB_TX_LATECOLLS 31 + +#define VGE_MIB_CNT (VGE_MIB_TX_LATECOLLS - VGE_MIB_RX_FRAMES + 1) +#define VGE_MIB_DATA_MASK 0x00FFFFFF +#define VGE_MIB_DATA_IDX(x) ((x) >> 24) + +/* Sticky bit shadow register */ + +#define VGE_STICKHW_DS0 0x01 +#define VGE_STICKHW_DS1 0x02 +#define VGE_STICKHW_WOL_ENB 0x04 +#define VGE_STICKHW_WOL_STS 0x08 +#define VGE_STICKHW_SWPTAG 0x10 + +/* WOL pattern control */ +#define VGE_WOLCR0_PATTERN0 0x01 +#define VGE_WOLCR0_PATTERN1 0x02 +#define VGE_WOLCR0_PATTERN2 0x04 +#define VGE_WOLCR0_PATTERN3 0x08 +#define VGE_WOLCR0_PATTERN4 0x10 +#define VGE_WOLCR0_PATTERN5 0x20 +#define VGE_WOLCR0_PATTERN6 0x40 +#define VGE_WOLCR0_PATTERN7 0x80 +#define VGE_WOLCR0_PATTERN_ALL 0xFF + +/* WOL event control */ +#define VGE_WOLCR1_UCAST 0x01 +#define VGE_WOLCR1_MAGIC 0x02 +#define VGE_WOLCR1_LINKON 0x04 +#define VGE_WOLCR1_LINKOFF 0x08 + +/* Poweer management config */ +#define VGE_PWRCFG_LEGACY_WOLEN 0x01 +#define VGE_PWRCFG_WOL_PULSE 0x20 +#define VGE_PWRCFG_WOL_BUTTON 0x00 + +/* WOL config register */ +#define VGE_WOLCFG_PHYINT_ENB 0x01 +#define VGE_WOLCFG_SAB 0x10 +#define VGE_WOLCFG_SAM 0x20 +#define VGE_WOLCFG_PMEOVR 0x80 + /* EEPROM control/status register */ #define VGE_EECSR_EDO 0x01 /* data out pin */ @@ -587,8 +670,7 @@ struct vge_tx_frag { uint32_t vge_addrlo; - uint16_t vge_addrhi; - uint16_t vge_buflen; + uint32_t vge_addrhi; }; /* @@ -600,7 +682,7 @@ struct vge_tx_frag { * to obtain this behavior, the special 'queue' bit must be set. */ -#define VGE_TXDESC_Q 0x8000 +#define VGE_TXDESC_Q 0x80000000 struct vge_tx_desc { uint32_t vge_sts; @@ -645,11 +727,10 @@ struct vge_tx_desc { /* 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; + uint32_t vge_sts; + uint32_t vge_ctl; + uint32_t vge_addrlo; + uint32_t vge_addrhi; }; /* @@ -658,7 +739,7 @@ struct vge_rx_desc { * not interrupts are generated for this descriptor. */ -#define VGE_RXDESC_I 0x8000 +#define VGE_RXDESC_I 0x80000000 #define VGE_RDSTS_VIDM 0x00000001 /* VLAN tag filter miss */ #define VGE_RDSTS_CRCERR 0x00000002 /* bad CRC error */ @@ -680,8 +761,8 @@ struct vge_rx_desc { #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_EOF 0x00000100 /* last frag in frame */ +#define VGE_RXPKT_SOF 0x00000200 /* first frag in frame */ #define VGE_RXPKT_MOF 0x00000300 /* intermediate frag */ #define VGE_RDCTL_VLANID 0x0000FFFF /* VLAN ID info */ diff --git a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgevar.h b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgevar.h index c4224f7eff..d73370b50a 100644 --- a/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgevar.h +++ b/src/add-ons/kernel/drivers/network/vt612x/dev/vge/if_vgevar.h @@ -29,37 +29,55 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/sys/dev/vge/if_vgevar.h,v 1.4.22.9.4.1 2010/12/21 17:09:25 kensmith Exp $ */ -#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_DESC_CNT 252 /* Must be a multiple of 4!! */ +#define VGE_TX_RING_ALIGN 64 +#define VGE_RX_RING_ALIGN 64 +#define VGE_MAXTXSEGS 6 +#define VGE_RX_BUF_ALIGN sizeof(uint64_t) + +/* + * VIA Velocity allows 64bit DMA addressing but high 16bits + * of the DMA address should be the same for Tx/Rx buffers. + * Because this condition can't be guaranteed vge(4) limit + * DMA address space to 48bits. + */ +#if (BUS_SPACE_MAXADDR < 0xFFFFFFFFFF) +#define VGE_BUF_DMA_MAXADDR BUS_SPACE_MAXADDR +#else +#define VGE_BUF_DMA_MAXADDR 0xFFFFFFFFFFFF +#endif + #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_TX_DESC_INC(x) ((x) = ((x) + 1) % VGE_TX_DESC_CNT) +#define VGE_TX_DESC_DEC(x) \ + ((x) = (((x) + VGE_TX_DESC_CNT - 1) % VGE_TX_DESC_CNT)) +#define VGE_RX_DESC_INC(x) ((x) = ((x) + 1) % VGE_RX_DESC_CNT) +#define VGE_ADDR_LO(y) ((uint64_t) (y) & 0xFFFFFFFF) +#define VGE_ADDR_HI(y) ((uint64_t) (y) >> 32) +#define VGE_BUFLEN(y) ((y) & 0x3FFF) +#define VGE_RXBYTES(x) (((x) & 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 +#define VGE_INT_HOLDOFF_TICK 20 +#define VGE_INT_HOLDOFF_USEC(x) ((x) / VGE_INT_HOLDOFF_TICK) +#define VGE_INT_HOLDOFF_MIN 0 +#define VGE_INT_HOLDOFF_MAX (255 * VGE_INT_HOLDOFF_TICK) +#define VGE_INT_HOLDOFF_DEFAULT 150 + +#define VGE_RX_COAL_PKT_MIN 1 +#define VGE_RX_COAL_PKT_MAX VGE_RX_DESC_CNT +#define VGE_RX_COAL_PKT_DEFAULT 64 + +#define VGE_TX_COAL_PKT_MIN 1 +#define VGE_TX_COAL_PKT_MAX VGE_TX_DESC_CNT +#define VGE_TX_COAL_PKT_DEFAULT 128 struct vge_type { uint16_t vge_vid; @@ -67,64 +85,124 @@ struct vge_type { 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_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; + struct vge_tx_desc *tx_desc; + struct vge_txdesc *txd_prev; }; -struct vge_list_data { - struct mbuf *vge_tx_mbuf[VGE_TX_DESC_CNT]; - struct mbuf *vge_rx_mbuf[VGE_RX_DESC_CNT]; +struct vge_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; + struct vge_rx_desc *rx_desc; + struct vge_rxdesc *rxd_prev; +}; + +struct vge_chain_data{ + bus_dma_tag_t vge_ring_tag; + bus_dma_tag_t vge_buffer_tag; + bus_dma_tag_t vge_tx_tag; + struct vge_txdesc vge_txdesc[VGE_TX_DESC_CNT]; + bus_dma_tag_t vge_rx_tag; + struct vge_rxdesc vge_rxdesc[VGE_RX_DESC_CNT]; + bus_dma_tag_t vge_tx_ring_tag; + bus_dmamap_t vge_tx_ring_map; + bus_dma_tag_t vge_rx_ring_tag; + bus_dmamap_t vge_rx_ring_map; + bus_dmamap_t vge_rx_sparemap; + 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; + int vge_tx_cnt; + int vge_rx_prodidx; + int vge_rx_commit; + + struct mbuf *vge_head; + struct mbuf *vge_tail; +}; + +#define VGE_CHAIN_RESET(_sc) \ +do { \ + if ((_sc)->vge_cdata.vge_head != NULL) { \ + m_freem((_sc)->vge_cdata.vge_head); \ + (_sc)->vge_cdata.vge_head = NULL; \ + (_sc)->vge_cdata.vge_tail = NULL; \ + } \ +} while (0); + +struct vge_ring_data { + struct vge_tx_desc *vge_tx_ring; + bus_addr_t vge_tx_ring_paddr; + struct vge_rx_desc *vge_rx_ring; + bus_addr_t vge_rx_ring_paddr; +}; + +struct vge_hw_stats { + uint32_t rx_frames; + uint32_t rx_good_frames; + uint32_t rx_fifo_oflows; + uint32_t rx_runts; + uint32_t rx_runts_errs; + uint32_t rx_pkts_64; + uint32_t rx_pkts_65_127; + uint32_t rx_pkts_128_255; + uint32_t rx_pkts_256_511; + uint32_t rx_pkts_512_1023; + uint32_t rx_pkts_1024_1518; + uint32_t rx_pkts_1519_max; + uint32_t rx_pkts_1519_max_errs; + uint32_t rx_jumbos; + uint32_t rx_crcerrs; + uint32_t rx_pause_frames; + uint32_t rx_alignerrs; + uint32_t rx_nobufs; + uint32_t rx_symerrs; + uint32_t rx_lenerrs; + + uint32_t tx_good_frames; + uint32_t tx_pkts_64; + uint32_t tx_pkts_65_127; + uint32_t tx_pkts_128_255; + uint32_t tx_pkts_256_511; + uint32_t tx_pkts_512_1023; + uint32_t tx_pkts_1024_1518; + uint32_t tx_jumbos; + uint32_t tx_colls; + uint32_t tx_pause; + uint32_t tx_sqeerrs; + uint32_t tx_latecolls; }; struct vge_softc { struct ifnet *vge_ifp; /* 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_phyaddr; + int vge_flags; +#define VGE_FLAG_PCIE 0x0001 +#define VGE_FLAG_MSI 0x0002 +#define VGE_FLAG_PMCAP 0x0004 +#define VGE_FLAG_JUMBO 0x0008 +#define VGE_FLAG_SUSPENDED 0x4000 +#define VGE_FLAG_LINK 0x8000 + int vge_expcap; + int vge_pmcap; int vge_camidx; - struct task vge_txtask; + int vge_int_holdoff; + int vge_rx_coal_pkt; + int vge_tx_coal_pkt; struct mtx vge_mtx; - struct mbuf *vge_head; - struct mbuf *vge_tail; + struct callout vge_watchdog; + int vge_timer; - struct vge_list_data vge_ldata; - - int suspended; /* 0 = normal 1 = suspended */ -#ifdef DEVICE_POLLING - int rxcycles; -#endif + struct vge_chain_data vge_cdata; + struct vge_ring_data vge_rdata; + struct vge_hw_stats vge_stats; }; #define VGE_LOCK(_sc) mtx_lock(&(_sc)->vge_mtx) @@ -135,20 +213,20 @@ struct vge_softc { * 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) + bus_write_stream_4(sc->vge_res, reg, val) #define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->vge_btag, sc->vge_bhandle, reg, val) + bus_write_4(sc->vge_res, reg, val) #define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->vge_btag, sc->vge_bhandle, reg, val) + bus_write_2(sc->vge_res, reg, val) #define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->vge_btag, sc->vge_bhandle, reg, val) + bus_write_1(sc->vge_res, reg, val) #define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->vge_btag, sc->vge_bhandle, reg) + bus_read_4(sc->vge_res, reg) #define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->vge_btag, sc->vge_bhandle, reg) + bus_read_2(sc->vge_res, reg) #define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->vge_btag, sc->vge_bhandle, reg) + bus_read_1(sc->vge_res, reg) #define CSR_SETBIT_1(sc, reg, x) \ CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x)) @@ -164,4 +242,6 @@ struct vge_softc { #define CSR_CLRBIT_4(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) +#define VGE_RXCHUNK 4 #define VGE_TIMEOUT 10000 +