Author: yongari
Date: Sun Aug 15 23:13:09 2010
New Revision: 211378
URL: http://svn.freebsd.org/changeset/base/211378

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

Modified:
  stable/7/sys/pci/if_rl.c
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/pci/if_rl.c
==============================================================================
--- stable/7/sys/pci/if_rl.c    Sun Aug 15 23:11:38 2010        (r211377)
+++ stable/7/sys/pci/if_rl.c    Sun Aug 15 23:13:09 2010        (r211378)
@@ -221,6 +221,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
@@ -800,7 +802,7 @@ rl_attach(device_t dev)
        struct ifnet            *ifp;
        struct rl_softc         *sc;
        struct rl_type          *t;
-       int                     error = 0, i, rid;
+       int                     error = 0, hwrev, i, pmc, rid;
        int                     unit;
        uint16_t                rl_did = 0;
 
@@ -926,6 +928,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;
@@ -1776,7 +1797,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:
@@ -1803,6 +1824,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)) {
@@ -1828,6 +1850,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);
@@ -1916,6 +1947,7 @@ rl_suspend(device_t dev)
 
        RL_LOCK(sc);
        rl_stop(sc);
+       rl_setwol(sc);
        sc->suspended = 1;
        RL_UNLOCK(sc);
 
@@ -1932,12 +1964,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);
@@ -1962,7 +2013,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);
+}
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to