Date: Thu, 18 Nov 2010 17:49:24 +0000 (UTC) From: Pyun YongHyeon <yongari@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org Subject: svn commit: r215457 - stable/7/sys/dev/nfe Message-ID: <201011181749.oAIHnOCj039811@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: yongari Date: Thu Nov 18 17:49:24 2010 New Revision: 215457 URL: http://svn.freebsd.org/changeset/base/215457 Log: MFC r215132,215194: r215132: Add basic WOL support for MCP ethernet controllers. It seems the controller does not perform automatic switching from 1000Mbps link to 10/100Mbps link when WOL is activated. Implement establishing 10/100Mps link with auto-negotiation in driver. Link status change handler was modified to remove taskqueue based approach since driver now needs synchronous handling for link establishment. Submitted by: Yamagi Burmeister (lists <> yamagi.org ) (initial version) Tested by: Yamagi Burmeister (lists <> yamagi.org ) r215194: Fix regression introduced in r215132. Caller always holds a driver lock before accessing mii, so locking is not needed here. Reported by: Rob Farmer (rfarmer <> predatorlabs dot net) Modified: stable/7/sys/dev/nfe/if_nfe.c stable/7/sys/dev/nfe/if_nfereg.h stable/7/sys/dev/nfe/if_nfevar.h Directory Properties: stable/7/sys/ (props changed) stable/7/sys/cddl/contrib/opensolaris/ (props changed) stable/7/sys/contrib/dev/acpica/ (props changed) stable/7/sys/contrib/pf/ (props changed) Modified: stable/7/sys/dev/nfe/if_nfe.c ============================================================================== --- stable/7/sys/dev/nfe/if_nfe.c Thu Nov 18 17:44:02 2010 (r215456) +++ stable/7/sys/dev/nfe/if_nfe.c Thu Nov 18 17:49:24 2010 (r215457) @@ -81,7 +81,7 @@ static void nfe_power(struct nfe_softc * static int nfe_miibus_readreg(device_t, int, int); static int nfe_miibus_writereg(device_t, int, int, int); static void nfe_miibus_statchg(device_t); -static void nfe_link_task(void *, int); +static void nfe_mac_config(struct nfe_softc *, struct mii_data *); static void nfe_set_intr(struct nfe_softc *); static __inline void nfe_enable_intr(struct nfe_softc *); static __inline void nfe_disable_intr(struct nfe_softc *); @@ -125,6 +125,8 @@ static int sysctl_hw_nfe_proc_limit(SYSC static void nfe_sysctl_node(struct nfe_softc *); static void nfe_stats_clear(struct nfe_softc *); static void nfe_stats_update(struct nfe_softc *); +static void nfe_set_linkspeed(struct nfe_softc *); +static void nfe_set_wol(struct nfe_softc *); #ifdef NFE_DEBUG static int nfedebug = 0; @@ -348,7 +350,6 @@ nfe_attach(device_t dev) mtx_init(&sc->nfe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->nfe_stat_ch, &sc->nfe_mtx, 0); - TASK_INIT(&sc->nfe_link_task, 0, nfe_link_task, sc); pci_enable_busmaster(dev); @@ -587,6 +588,9 @@ nfe_attach(device_t dev) if ((ifp->if_capabilities & IFCAP_HWCSUM) != 0) ifp->if_capabilities |= IFCAP_VLAN_HWCSUM; } + + if (pci_find_extcap(dev, PCIY_PMG, ®) == 0) + ifp->if_capabilities |= IFCAP_WOL_MAGIC; ifp->if_capenable = ifp->if_capabilities; /* @@ -667,7 +671,6 @@ nfe_detach(device_t dev) NFE_UNLOCK(sc); callout_drain(&sc->nfe_stat_ch); taskqueue_drain(taskqueue_fast, &sc->nfe_tx_task); - taskqueue_drain(taskqueue_swi, &sc->nfe_link_task); ether_ifdetach(ifp); } @@ -753,6 +756,7 @@ nfe_suspend(device_t dev) NFE_LOCK(sc); nfe_stop(sc->nfe_ifp); + nfe_set_wol(sc); sc->nfe_suspended = 1; NFE_UNLOCK(sc); @@ -769,6 +773,7 @@ nfe_resume(device_t dev) sc = device_get_softc(dev); NFE_LOCK(sc); + nfe_power(sc); ifp = sc->nfe_ifp; if (ifp->if_flags & IFF_UP) nfe_init_locked(sc); @@ -807,37 +812,51 @@ static void nfe_miibus_statchg(device_t dev) { struct nfe_softc *sc; - - sc = device_get_softc(dev); - taskqueue_enqueue(taskqueue_swi, &sc->nfe_link_task); -} - - -static void -nfe_link_task(void *arg, int pending) -{ - struct nfe_softc *sc; struct mii_data *mii; struct ifnet *ifp; - uint32_t phy, seed, misc = NFE_MISC1_MAGIC, link = NFE_MEDIA_SET; - uint32_t gmask, rxctl, txctl, val; + uint32_t rxctl, txctl; - sc = (struct nfe_softc *)arg; - - NFE_LOCK(sc); + sc = device_get_softc(dev); mii = device_get_softc(sc->nfe_miibus); ifp = sc->nfe_ifp; - if (mii == NULL || ifp == NULL) { - NFE_UNLOCK(sc); - return; - } - if (mii->mii_media_status & IFM_ACTIVE) { - if (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) + sc->nfe_link = 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: + case IFM_1000_T: sc->nfe_link = 1; - } else - sc->nfe_link = 0; + break; + default: + break; + } + } + + nfe_mac_config(sc, mii); + txctl = NFE_READ(sc, NFE_TX_CTL); + rxctl = NFE_READ(sc, NFE_RX_CTL); + if (sc->nfe_link != 0 && (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + txctl |= NFE_TX_START; + rxctl |= NFE_RX_START; + } else { + txctl &= ~NFE_TX_START; + rxctl &= ~NFE_RX_START; + } + NFE_WRITE(sc, NFE_TX_CTL, txctl); + NFE_WRITE(sc, NFE_RX_CTL, rxctl); +} + + +static void +nfe_mac_config(struct nfe_softc *sc, struct mii_data *mii) +{ + uint32_t link, misc, phy, seed; + uint32_t val; + + NFE_LOCK_ASSERT(sc); phy = NFE_READ(sc, NFE_PHY_IFACE); phy &= ~(NFE_PHY_HDX | NFE_PHY_100TX | NFE_PHY_1000T); @@ -845,7 +864,10 @@ nfe_link_task(void *arg, int pending) seed = NFE_READ(sc, NFE_RNDSEED); seed &= ~NFE_SEED_MASK; - if (((mii->mii_media_active & IFM_GMASK) & IFM_FDX) == 0) { + misc = NFE_MISC1_MAGIC; + link = NFE_MEDIA_SET; + + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) == 0) { phy |= NFE_PHY_HDX; /* half-duplex */ misc |= NFE_MISC1_HDX; } @@ -882,18 +904,18 @@ nfe_link_task(void *arg, int pending) NFE_WRITE(sc, NFE_MISC1, misc); NFE_WRITE(sc, NFE_LINKSPEED, link); - gmask = mii->mii_media_active & IFM_GMASK; - if ((gmask & IFM_FDX) != 0) { + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { /* It seems all hardwares supports Rx pause frames. */ val = NFE_READ(sc, NFE_RXFILTER); - if ((gmask & IFM_FLAG0) != 0) + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FLAG0) != 0) val |= NFE_PFF_RX_PAUSE; else val &= ~NFE_PFF_RX_PAUSE; NFE_WRITE(sc, NFE_RXFILTER, val); if ((sc->nfe_flags & NFE_TX_FLOW_CTRL) != 0) { val = NFE_READ(sc, NFE_MISC1); - if ((gmask & IFM_FLAG1) != 0) { + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_FLAG1) != 0) { NFE_WRITE(sc, NFE_TX_PAUSE_FRAME, NFE_TX_PAUSE_FRAME_ENABLE); val |= NFE_MISC1_TX_PAUSE; @@ -917,20 +939,6 @@ nfe_link_task(void *arg, int pending) NFE_WRITE(sc, NFE_MISC1, val); } } - - txctl = NFE_READ(sc, NFE_TX_CTL); - rxctl = NFE_READ(sc, NFE_RX_CTL); - if (sc->nfe_link != 0 && (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { - txctl |= NFE_TX_START; - rxctl |= NFE_RX_START; - } else { - txctl &= ~NFE_TX_START; - rxctl &= ~NFE_RX_START; - } - NFE_WRITE(sc, NFE_TX_CTL, txctl); - NFE_WRITE(sc, NFE_RX_CTL, rxctl); - - NFE_UNLOCK(sc); } @@ -1713,6 +1721,10 @@ nfe_ioctl(struct ifnet *ifp, u_long cmd, } } #endif /* DEVICE_POLLING */ + if ((mask & IFCAP_WOL_MAGIC) != 0 && + (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) + ifp->if_capenable ^= IFCAP_WOL_MAGIC; + if ((sc->nfe_flags & NFE_HW_CSUM) != 0 && (mask & IFCAP_HWCSUM) != 0) { ifp->if_capenable ^= IFCAP_HWCSUM; @@ -2737,7 +2749,8 @@ nfe_init_locked(void *xsc) NFE_WRITE(sc, NFE_STATUS, sc->mii_phyaddr << 24 | NFE_STATUS_MAGIC); NFE_WRITE(sc, NFE_SETUP_R4, NFE_R4_MAGIC); - NFE_WRITE(sc, NFE_WOL_CTL, NFE_WOL_MAGIC); + /* Disable WOL. */ + NFE_WRITE(sc, NFE_WOL_CTL, 0); sc->rxtxctl &= ~NFE_RXTX_BIT2; NFE_WRITE(sc, NFE_RXTX_CTL, sc->rxtxctl); @@ -2908,18 +2921,8 @@ nfe_tick(void *xsc) static int nfe_shutdown(device_t dev) { - struct nfe_softc *sc; - struct ifnet *ifp; - - sc = device_get_softc(dev); - NFE_LOCK(sc); - ifp = sc->nfe_ifp; - nfe_stop(ifp); - /* nfe_reset(sc); */ - NFE_UNLOCK(sc); - - return (0); + return (nfe_suspend(dev)); } @@ -3203,3 +3206,115 @@ nfe_stats_update(struct nfe_softc *sc) stats->rx_broadcast += NFE_READ(sc, NFE_TX_BROADCAST); } } + + +static void +nfe_set_linkspeed(struct nfe_softc *sc) +{ + struct mii_softc *miisc; + struct mii_data *mii; + int aneg, i, phyno; + + NFE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->nfe_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++; + break; + default: + break; + } + } + phyno = 0; + if (mii->mii_instance) { + miisc = LIST_FIRST(&mii->mii_phys); + phyno = miisc->mii_phy; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } else + return; + nfe_miibus_writereg(sc->nfe_dev, phyno, MII_100T2CR, 0); + nfe_miibus_writereg(sc->nfe_dev, phyno, + MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA); + nfe_miibus_writereg(sc->nfe_dev, phyno, + MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG); + DELAY(1000); + if (aneg != 0) { + /* + * Poll link state until nfe(4) get a 10/100Mbps 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: + nfe_mac_config(sc, mii); + return; + default: + break; + } + } + NFE_UNLOCK(sc); + pause("nfelnk", hz); + NFE_LOCK(sc); + } + if (i == MII_ANEGTICKS_GIGE) + device_printf(sc->nfe_dev, + "establishing a 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; + nfe_mac_config(sc, mii); +} + + +static void +nfe_set_wol(struct nfe_softc *sc) +{ + struct ifnet *ifp; + uint32_t wolctl; + int pmc; + uint16_t pmstat; + + NFE_LOCK_ASSERT(sc); + + if (pci_find_extcap(sc->nfe_dev, PCIY_PMG, &pmc) != 0) + return; + ifp = sc->nfe_ifp; + if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) + wolctl = NFE_WOL_MAGIC; + else + wolctl = 0; + NFE_WRITE(sc, NFE_WOL_CTL, wolctl); + if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) { + nfe_set_linkspeed(sc); + if ((sc->nfe_flags & NFE_PWR_MGMT) != 0) + NFE_WRITE(sc, NFE_PWR2_CTL, + NFE_READ(sc, NFE_PWR2_CTL) & ~NFE_PWR2_GATE_CLOCKS); + /* Enable RX. */ + NFE_WRITE(sc, NFE_RX_RING_ADDR_HI, 0); + NFE_WRITE(sc, NFE_RX_RING_ADDR_LO, 0); + NFE_WRITE(sc, NFE_RX_CTL, NFE_READ(sc, NFE_RX_CTL) | + NFE_RX_START); + } + /* Request PME if WOL is requested. */ + pmstat = pci_read_config(sc->nfe_dev, pmc + 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->nfe_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); +} Modified: stable/7/sys/dev/nfe/if_nfereg.h ============================================================================== --- stable/7/sys/dev/nfe/if_nfereg.h Thu Nov 18 17:44:02 2010 (r215456) +++ stable/7/sys/dev/nfe/if_nfereg.h Thu Nov 18 17:49:24 2010 (r215457) @@ -190,6 +190,7 @@ #define NFE_PWR2_WAKEUP_MASK 0x0f11 #define NFE_PWR2_REVA3 (1 << 0) +#define NFE_PWR2_GATE_CLOCKS 0x0f00 #define NFE_MEDIA_SET 0x10000 #define NFE_MEDIA_1000T 0x00032 Modified: stable/7/sys/dev/nfe/if_nfevar.h ============================================================================== --- stable/7/sys/dev/nfe/if_nfevar.h Thu Nov 18 17:44:02 2010 (r215456) +++ stable/7/sys/dev/nfe/if_nfevar.h Thu Nov 18 17:49:24 2010 (r215457) @@ -140,7 +140,6 @@ struct nfe_softc { struct taskqueue *nfe_tq; struct task nfe_int_task; struct task nfe_tx_task; - struct task nfe_link_task; int nfe_link; int nfe_suspended; int nfe_framesize;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201011181749.oAIHnOCj039811>