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;
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to