From owner-svn-src-all@FreeBSD.ORG Fri Dec 18 22:14:28 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 8223C1065672; Fri, 18 Dec 2009 22:14:28 +0000 (UTC) (envelope-from yongari@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 701788FC15; Fri, 18 Dec 2009 22:14:28 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id nBIMESkG010520; Fri, 18 Dec 2009 22:14:28 GMT (envelope-from yongari@svn.freebsd.org) Received: (from yongari@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id nBIMESsh010515; Fri, 18 Dec 2009 22:14:28 GMT (envelope-from yongari@svn.freebsd.org) Message-Id: <200912182214.nBIMESsh010515@svn.freebsd.org> From: Pyun YongHyeon Date: Fri, 18 Dec 2009 22:14:28 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r200696 - head/sys/dev/vge X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 18 Dec 2009 22:14:28 -0000 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;