Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 18 Dec 2009 22:14:28 +0000 (UTC)
From:      Pyun YongHyeon <yongari@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r200696 - head/sys/dev/vge
Message-ID:  <200912182214.nBIMESsh010515@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: yongari
Date: Fri Dec 18 22:14:28 2009
New Revision: 200696
URL: http://svn.freebsd.org/changeset/base/200696

Log:
  Add rudimentary WOL support. While I'm here remove enabling
  busmastering/memory address in resume path. Bus driver will handle
  that.

Modified:
  head/sys/dev/vge/if_vge.c
  head/sys/dev/vge/if_vgereg.h
  head/sys/dev/vge/if_vgevar.h

Modified: head/sys/dev/vge/if_vge.c
==============================================================================
--- head/sys/dev/vge/if_vge.c	Fri Dec 18 22:13:34 2009	(r200695)
+++ head/sys/dev/vge/if_vge.c	Fri Dec 18 22:14:28 2009	(r200696)
@@ -157,6 +157,7 @@ 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 *);
@@ -190,6 +191,7 @@ static int	vge_rx_list_init(struct vge_s
 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 *);
@@ -1012,6 +1014,10 @@ vge_attach(device_t dev)
 		sc->vge_flags |= VGE_FLAG_PCIE;
 		sc->vge_expcap = cap;
 	}
+	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) {
@@ -1069,6 +1075,8 @@ vge_attach(device_t dev)
 	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)
@@ -1098,6 +1106,8 @@ vge_attach(device_t dev)
 	ifp->if_hwassist = VGE_CSUM_FEATURES;
 	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;
@@ -2279,6 +2289,15 @@ vge_ioctl(struct ifnet *ifp, u_long comm
 		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;
@@ -2365,7 +2384,7 @@ vge_suspend(device_t dev)
 
 	VGE_LOCK(sc);
 	vge_stop(sc);
-
+	vge_setwol(sc);
 	sc->vge_flags |= VGE_FLAG_SUSPENDED;
 	VGE_UNLOCK(sc);
 
@@ -2382,17 +2401,26 @@ vge_resume(device_t dev)
 {
 	struct vge_softc *sc;
 	struct ifnet *ifp;
+	uint16_t pmstat;
 
 	sc = device_get_softc(dev);
-	ifp = sc->vge_ifp;
-
-	/* reenable busmastering */
-	pci_enable_busmaster(dev);
-	pci_enable_io(dev, SYS_RES_MEMORY);
-
-	/* reinitialize interface if necessary */
 	VGE_LOCK(sc);
-	if (ifp->if_flags & IFF_UP) {
+	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;
+	/* Reinitialize interface if necessary. */
+	if ((ifp->if_flags & IFF_UP) != 0) {
 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 		vge_init_locked(sc);
 	}
@@ -2409,15 +2437,8 @@ vge_resume(device_t dev)
 static int
 vge_shutdown(device_t dev)
 {
-	struct vge_softc *sc;
-
-	sc = device_get_softc(dev);
 
-	VGE_LOCK(sc);
-	vge_stop(sc);
-	VGE_UNLOCK(sc);
-
-	return (0);
+	return (vge_suspend(dev));
 }
 
 #define	VGE_SYSCTL_STAT_ADD32(c, h, n, p, d)	\
@@ -2706,3 +2727,154 @@ vge_intr_holdoff(struct vge_softc *sc)
 		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);
+}

Modified: head/sys/dev/vge/if_vgereg.h
==============================================================================
--- head/sys/dev/vge/if_vgereg.h	Fri Dec 18 22:13:34 2009	(r200695)
+++ head/sys/dev/vge/if_vgereg.h	Fri Dec 18 22:14:28 2009	(r200696)
@@ -590,6 +590,42 @@
 #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 */

Modified: head/sys/dev/vge/if_vgevar.h
==============================================================================
--- head/sys/dev/vge/if_vgevar.h	Fri Dec 18 22:13:34 2009	(r200695)
+++ head/sys/dev/vge/if_vgevar.h	Fri Dec 18 22:14:28 2009	(r200696)
@@ -186,9 +186,11 @@ struct vge_softc {
 	int			vge_flags;
 #define	VGE_FLAG_PCIE		0x0001
 #define	VGE_FLAG_MSI		0x0002
+#define	VGE_FLAG_PMCAP		0x0004
 #define	VGE_FLAG_SUSPENDED	0x4000
 #define	VGE_FLAG_LINK		0x8000
 	int			vge_expcap;
+	int			vge_pmcap;
 	int			vge_camidx;
 	int			vge_int_holdoff;
 	int			vge_rx_coal_pkt;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912182214.nBIMESsh010515>