Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 19 Jul 2010 18:01:06 +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: r210244 - head/sys/pci
Message-ID:  <201007191801.o6JI167C040261@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: yongari
Date: Mon Jul 19 18:01:06 2010
New Revision: 210244
URL: http://svn.freebsd.org/changeset/base/210244

Log:
  Implement WOL. WOL is supported on RTL8139B or newer controllers.
  
  PR:	kern/148013

Modified:
  head/sys/pci/if_rl.c

Modified: head/sys/pci/if_rl.c
==============================================================================
--- head/sys/pci/if_rl.c	Mon Jul 19 16:38:45 2010	(r210243)
+++ head/sys/pci/if_rl.c	Mon Jul 19 18:01:06 2010	(r210244)
@@ -222,6 +222,8 @@ static int rl_suspend(device_t);
 static void rl_tick(void *);
 static void rl_txeof(struct rl_softc *);
 static void rl_watchdog(struct rl_softc *);
+static void rl_setwol(struct rl_softc *);
+static void rl_clrwol(struct rl_softc *);
 
 #ifdef RL_USEIOSPACE
 #define RL_RES			SYS_RES_IOPORT
@@ -803,7 +805,7 @@ rl_attach(device_t dev)
 	struct rl_type		*t;
 	struct sysctl_ctx_list	*ctx;
 	struct sysctl_oid_list	*children;
-	int			error = 0, i, rid;
+	int			error = 0, hwrev, i, pmc, rid;
 	int			unit;
 	uint16_t		rl_did = 0;
 	char			tn[32];
@@ -938,6 +940,25 @@ rl_attach(device_t dev)
 	ifp->if_start = rl_start;
 	ifp->if_init = rl_init;
 	ifp->if_capabilities = IFCAP_VLAN_MTU;
+	/* Check WOL for RTL8139B or newer controllers. */
+	if (sc->rl_type == RL_8139 &&
+	    pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) == 0) {
+		hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV;
+		switch (hwrev) {
+		case RL_HWREV_8139B:
+		case RL_HWREV_8130:
+		case RL_HWREV_8139C:
+		case RL_HWREV_8139D:
+		case RL_HWREV_8101:
+		case RL_HWREV_8100:
+			ifp->if_capabilities |= IFCAP_WOL;
+			/* Disable WOL. */
+			rl_clrwol(sc);
+			break;
+		default:
+			break;
+		}
+	}
 	ifp->if_capenable = ifp->if_capabilities;
 #ifdef DEVICE_POLLING
 	ifp->if_capabilities |= IFCAP_POLLING;
@@ -1926,7 +1947,7 @@ rl_ioctl(struct ifnet *ifp, u_long comma
 	struct ifreq		*ifr = (struct ifreq *)data;
 	struct mii_data		*mii;
 	struct rl_softc		*sc = ifp->if_softc;
-	int			error = 0;
+	int			error = 0, mask;
 
 	switch (command) {
 	case SIOCSIFFLAGS:
@@ -1953,6 +1974,7 @@ rl_ioctl(struct ifnet *ifp, u_long comma
 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
 		break;
 	case SIOCSIFCAP:
+		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
 #ifdef DEVICE_POLLING
 		if (ifr->ifr_reqcap & IFCAP_POLLING &&
 		    !(ifp->if_capenable & IFCAP_POLLING)) {
@@ -1978,6 +2000,15 @@ rl_ioctl(struct ifnet *ifp, u_long comma
 			return (error);
 		}
 #endif /* DEVICE_POLLING */
+		if ((mask & IFCAP_WOL) != 0 &&
+		    (ifp->if_capabilities & IFCAP_WOL) != 0) {
+			if ((mask & IFCAP_WOL_UCAST) != 0)
+				ifp->if_capenable ^= IFCAP_WOL_UCAST;
+			if ((mask & IFCAP_WOL_MCAST) != 0)
+				ifp->if_capenable ^= IFCAP_WOL_MCAST;
+			if ((mask & IFCAP_WOL_MAGIC) != 0)
+				ifp->if_capenable ^= IFCAP_WOL_MAGIC;
+		}
 		break;
 	default:
 		error = ether_ioctl(ifp, command, data);
@@ -2066,6 +2097,7 @@ rl_suspend(device_t dev)
 
 	RL_LOCK(sc);
 	rl_stop(sc);
+	rl_setwol(sc);
 	sc->suspended = 1;
 	RL_UNLOCK(sc);
 
@@ -2082,12 +2114,31 @@ rl_resume(device_t dev)
 {
 	struct rl_softc		*sc;
 	struct ifnet		*ifp;
+	int			pmc;
+	uint16_t		pmstat;
 
 	sc = device_get_softc(dev);
 	ifp = sc->rl_ifp;
 
 	RL_LOCK(sc);
 
+	if ((ifp->if_capabilities & IFCAP_WOL) != 0 &&
+	    pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) == 0) {
+		/* Disable PME and clear PME status. */
+		pmstat = pci_read_config(sc->rl_dev,
+		    pmc + PCIR_POWER_STATUS, 2);
+		if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) {
+			pmstat &= ~PCIM_PSTAT_PMEENABLE;
+			pci_write_config(sc->rl_dev,
+			    pmc + PCIR_POWER_STATUS, pmstat, 2);
+		}
+		/*
+		 * Clear WOL matching such that normal Rx filtering
+		 * wouldn't interfere with WOL patterns.
+		 */
+		rl_clrwol(sc);
+	}
+
 	/* reinitialize interface if necessary */
 	if (ifp->if_flags & IFF_UP)
 		rl_init_locked(sc);
@@ -2112,7 +2163,93 @@ rl_shutdown(device_t dev)
 
 	RL_LOCK(sc);
 	rl_stop(sc);
+	/*
+	 * Mark interface as down since otherwise we will panic if
+	 * interrupt comes in later on, which can happen in some
+	 * cases.
+	 */
+	sc->rl_ifp->if_flags &= ~IFF_UP;
+	rl_setwol(sc);
 	RL_UNLOCK(sc);
 
 	return (0);
 }
+
+static void
+rl_setwol(struct rl_softc *sc)
+{
+	struct ifnet		*ifp;
+	int			pmc;
+	uint16_t		pmstat;
+	uint8_t			v;
+
+	RL_LOCK_ASSERT(sc);
+
+	ifp = sc->rl_ifp;
+	if ((ifp->if_capabilities & IFCAP_WOL) == 0)
+		return;
+	if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0)
+		return;
+
+	/* Enable config register write. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+
+	/* Enable PME. */
+	v = CSR_READ_1(sc, RL_CFG1);
+	v &= ~RL_CFG1_PME;
+	if ((ifp->if_capenable & IFCAP_WOL) != 0)
+		v |= RL_CFG1_PME;
+	CSR_WRITE_1(sc, RL_CFG1, v);
+
+	v = CSR_READ_1(sc, RL_CFG3);
+	v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
+	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
+		v |= RL_CFG3_WOL_MAGIC;
+	CSR_WRITE_1(sc, RL_CFG3, v);
+
+	/* Config register write done. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+	v = CSR_READ_1(sc, RL_CFG5);
+	v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
+	v &= ~RL_CFG5_WOL_LANWAKE;
+	if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0)
+		v |= RL_CFG5_WOL_UCAST;
+	if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0)
+		v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST;
+	if ((ifp->if_capenable & IFCAP_WOL) != 0)
+		v |= RL_CFG5_WOL_LANWAKE;
+	CSR_WRITE_1(sc, RL_CFG5, v);
+	/* Request PME if WOL is requested. */
+	pmstat = pci_read_config(sc->rl_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->rl_dev, pmc + PCIR_POWER_STATUS, pmstat, 2);
+}
+
+static void
+rl_clrwol(struct rl_softc *sc)
+{
+	struct ifnet		*ifp;
+	uint8_t			v;
+
+	ifp = sc->rl_ifp;
+	if ((ifp->if_capabilities & IFCAP_WOL) == 0)
+		return;
+
+	/* Enable config register write. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+
+	v = CSR_READ_1(sc, RL_CFG3);
+	v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
+	CSR_WRITE_1(sc, RL_CFG3, v);
+
+	/* Config register write done. */
+	CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+	v = CSR_READ_1(sc, RL_CFG5);
+	v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
+	v &= ~RL_CFG5_WOL_LANWAKE;
+	CSR_WRITE_1(sc, RL_CFG5, v);
+}



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