Author: yongari
Date: Fri Sep  3 00:34:45 2010
New Revision: 212167
URL: http://svn.freebsd.org/changeset/base/212167

Log:
  Initial WOL support. NS DP8315 was tested but SiS900/SiS7016 was
  not tested.
  While I'm here, clean up SIOCSIFCAP handler.

Modified:
  head/sys/dev/sis/if_sis.c
  head/sys/dev/sis/if_sisreg.h

Modified: head/sys/dev/sis/if_sis.c
==============================================================================
--- head/sys/dev/sis/if_sis.c   Thu Sep  2 22:37:13 2010        (r212166)
+++ head/sys/dev/sis/if_sis.c   Fri Sep  3 00:34:45 2010        (r212167)
@@ -147,12 +147,15 @@ static void sis_initl(struct sis_softc *
 static void sis_intr(void *);
 static int sis_ioctl(struct ifnet *, u_long, caddr_t);
 static int sis_newbuf(struct sis_softc *, struct sis_rxdesc *);
+static int sis_resume(device_t);
 static int sis_rxeof(struct sis_softc *);
 static void sis_start(struct ifnet *);
 static void sis_startl(struct ifnet *);
 static void sis_stop(struct sis_softc *);
+static int sis_suspend(device_t);
 static void sis_add_sysctls(struct sis_softc *);
 static void sis_watchdog(struct sis_softc *);
+static void sis_wol(struct sis_softc *);
 
 
 static struct resource_spec sis_res_spec[] = {
@@ -935,6 +938,9 @@ sis_reset(struct sis_softc *sc)
        if (sc->sis_type == SIS_TYPE_83815) {
                CSR_WRITE_4(sc, NS_CLKRUN, NS_CLKRUN_PMESTS);
                CSR_WRITE_4(sc, NS_CLKRUN, 0);
+       } else {
+               /* Disable WOL functions. */
+               CSR_WRITE_4(sc, SIS_PWRMAN_CTL, 0);
        }
 }
 
@@ -971,7 +977,7 @@ sis_attach(device_t dev)
        u_char                  eaddr[ETHER_ADDR_LEN];
        struct sis_softc        *sc;
        struct ifnet            *ifp;
-       int                     error = 0, waittime = 0;
+       int                     error = 0, pmc, waittime = 0;
 
        waittime = 0;
        sc = device_get_softc(dev);
@@ -1147,6 +1153,14 @@ sis_attach(device_t dev)
        ifp->if_snd.ifq_drv_maxlen = SIS_TX_LIST_CNT - 1;
        IFQ_SET_READY(&ifp->if_snd);
 
+       if (pci_find_extcap(sc->sis_dev, PCIY_PMG, &pmc) == 0) {
+               if (sc->sis_type == SIS_TYPE_83815)
+                       ifp->if_capabilities |= IFCAP_WOL;
+               else
+                       ifp->if_capabilities |= IFCAP_WOL_MAGIC;
+               ifp->if_capenable = ifp->if_capabilities;
+       }
+
        /*
         * Do MII setup.
         */
@@ -2227,7 +2241,7 @@ sis_ioctl(struct ifnet *ifp, u_long comm
        struct sis_softc        *sc = ifp->if_softc;
        struct ifreq            *ifr = (struct ifreq *) data;
        struct mii_data         *mii;
-       int                     error = 0;
+       int                     error = 0, mask;
 
        switch (command) {
        case SIOCSIFFLAGS:
@@ -2264,32 +2278,37 @@ sis_ioctl(struct ifnet *ifp, u_long comm
                error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
                break;
        case SIOCSIFCAP:
-               /* ok, disable interrupts */
+               SIS_LOCK(sc);
+               mask = ifr->ifr_reqcap ^ ifp->if_capenable;
 #ifdef DEVICE_POLLING
-               if (ifr->ifr_reqcap & IFCAP_POLLING &&
-                   !(ifp->if_capenable & IFCAP_POLLING)) {
-                       error = ether_poll_register(sis_poll, ifp);
-                       if (error)
-                               return (error);
-                       SIS_LOCK(sc);
-                       /* Disable interrupts */
-                       CSR_WRITE_4(sc, SIS_IER, 0);
-                       ifp->if_capenable |= IFCAP_POLLING;
-                       SIS_UNLOCK(sc);
-                       return (error);
-
-               }
-               if (!(ifr->ifr_reqcap & IFCAP_POLLING) &&
-                   ifp->if_capenable & IFCAP_POLLING) {
-                       error = ether_poll_deregister(ifp);
-                       /* Enable interrupts. */
-                       SIS_LOCK(sc);
-                       CSR_WRITE_4(sc, SIS_IER, 1);
-                       ifp->if_capenable &= ~IFCAP_POLLING;
-                       SIS_UNLOCK(sc);
-                       return (error);
+               if ((mask & IFCAP_POLLING) != 0 &&
+                   (IFCAP_POLLING & ifp->if_capabilities) != 0) {
+                       ifp->if_capenable ^= IFCAP_POLLING;
+                       if ((IFCAP_POLLING & ifp->if_capenable) != 0) {
+                               error = ether_poll_register(sis_poll, ifp);
+                               if (error != 0) {
+                                       SIS_UNLOCK(sc);
+                                       break;
+                               }
+                               /* Disable interrupts. */
+                               CSR_WRITE_4(sc, SIS_IER, 0);
+                        } else {
+                                error = ether_poll_deregister(ifp);
+                                /* Enable interrupts. */
+                               CSR_WRITE_4(sc, SIS_IER, 1);
+                        }
                }
 #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;
+               }
+               SIS_UNLOCK(sc);
                break;
        default:
                error = ether_ioctl(ifp, command, data);
@@ -2384,13 +2403,8 @@ sis_stop(struct sis_softc *sc)
 static int
 sis_shutdown(device_t dev)
 {
-       struct sis_softc        *sc;
 
-       sc = device_get_softc(dev);
-       SIS_LOCK(sc);
-       sis_stop(sc);
-       SIS_UNLOCK(sc);
-       return (0);
+       return (sis_suspend(dev));
 }
 
 static int
@@ -2401,6 +2415,7 @@ sis_suspend(device_t dev)
        sc = device_get_softc(dev);
        SIS_LOCK(sc);
        sis_stop(sc);
+       sis_wol(sc);
        SIS_UNLOCK(sc);
        return (0);
 }
@@ -2423,6 +2438,56 @@ sis_resume(device_t dev)
 }
 
 static void
+sis_wol(struct sis_softc *sc)
+{
+       struct ifnet            *ifp;
+       uint32_t                val;
+       uint16_t                pmstat;
+       int                     pmc;
+
+       ifp = sc->sis_ifp;
+       if ((ifp->if_capenable & IFCAP_WOL) == 0)
+               return;
+
+       if (sc->sis_type == SIS_TYPE_83815) {
+               /* Reset RXDP. */
+               CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0);
+
+               /* Configure WOL events. */
+               CSR_READ_4(sc, NS_WCSR);
+               val = 0;
+               if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0)
+                       val |= NS_WCSR_WAKE_UCAST;
+               if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0)
+                       val |= NS_WCSR_WAKE_MCAST;
+               if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
+                       val |= NS_WCSR_WAKE_MAGIC;
+               CSR_WRITE_4(sc, NS_WCSR, val);
+               /* Enable PME and clear PMESTS. */
+               val = CSR_READ_4(sc, NS_CLKRUN);
+               val |= NS_CLKRUN_PMEENB | NS_CLKRUN_PMESTS;
+               CSR_WRITE_4(sc, NS_CLKRUN, val);
+               /* Enable silent RX mode. */
+               SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE);
+       } else {
+               if (pci_find_extcap(sc->sis_dev, PCIY_PMG, &pmc) != 0)
+                       return;
+               val = 0;
+               if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
+                       val |= SIS_PWRMAN_WOL_MAGIC;
+               CSR_WRITE_4(sc, SIS_PWRMAN_CTL, val);
+               /* Request PME. */
+               pmstat = pci_read_config(sc->sis_dev,
+                   pmc + PCIR_POWER_STATUS, 2);
+               pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
+               if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
+                       pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
+               pci_write_config(sc->sis_dev,
+                   pmc + PCIR_POWER_STATUS, pmstat, 2);
+       }
+}
+
+static void
 sis_add_sysctls(struct sis_softc *sc)
 {
        struct sysctl_ctx_list *ctx;

Modified: head/sys/dev/sis/if_sisreg.h
==============================================================================
--- head/sys/dev/sis/if_sisreg.h        Thu Sep  2 22:37:13 2010        
(r212166)
+++ head/sys/dev/sis/if_sisreg.h        Fri Sep  3 00:34:45 2010        
(r212167)
@@ -77,6 +77,7 @@
 /* NS DP83815/6 registers */
 #define NS_IHR                 0x1C
 #define NS_CLKRUN              0x3C
+#define        NS_WCSR                 0x40
 #define NS_SRR                 0x58
 #define NS_BMCR                        0x80
 #define NS_BMSR                        0x84
@@ -99,6 +100,29 @@
 #define NS_CLKRUN_PMEENB       0x00000100
 #define NS_CLNRUN_CLKRUN_ENB   0x00000001
 
+#define        NS_WCSR_WAKE_PHYINTR    0x00000001
+#define        NS_WCSR_WAKE_UCAST      0x00000002
+#define        NS_WCSR_WAKE_MCAST      0x00000004
+#define        NS_WCSR_WAKE_BCAST      0x00000008
+#define        NS_WCSR_WAKE_ARP        0x00000010
+#define        NS_WCSR_WAKE_PATTERN0   0x00000020
+#define        NS_WCSR_WAKE_PATTERN1   0x00000040
+#define        NS_WCSR_WAKE_PATTERN2   0x00000080
+#define        NS_WCSR_WAKE_PATTERN3   0x00000100
+#define        NS_WCSR_WAKE_MAGIC      0x00000200
+#define        NS_WCSR_WAKE_MAGIC_SEC  0x00000400
+#define        NS_WCSR_DET_MAGIC_SECH  0x00100000
+#define        NS_WCSR_DET_PHYINTR     0x00400000
+#define        NS_WCSR_DET_UCAST       0x00800000
+#define        NS_WCSR_DET_MCAST       0x01000000
+#define        NS_WCSR_DET_BCAST       0x02000000
+#define        NS_WCSR_DET_ARP         0x04000000
+#define        NS_WCSR_DET_PATTERN0    0x08000000
+#define        NS_WCSR_DET_PATTERN1    0x10000000
+#define        NS_WCSR_DET_PATTERN2    0x20000000
+#define        NS_WCSR_DET_PATTERN3    0x40000000
+#define        NS_WCSR_DET_MAGIC       0x80000000
+
 /* NS silicon revisions */
 #define NS_SRR_15C             0x302
 #define NS_SRR_15D             0x403
@@ -303,6 +327,10 @@
 #define NS_FILTADDR_FMEM_LO    0x00000200
 #define NS_FILTADDR_FMEM_HI    0x000003FE
 
+#define        SIS_PWRMAN_WOL_LINK_OFF 0x00000001
+#define        SIS_PWRMAN_WOL_LINK_ON  0x00000002
+#define        SIS_PWRMAN_WOL_MAGIC    0x00000400
+
 /*
  * TX/RX DMA descriptor structures.
  */
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to