Skip site navigation (1)Skip section navigation (2)
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, &reg) == 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>