Folks, I discussed this topic with stsp@ some months ago, without doing a real follow-up.
The bge driver lacks WOL support in the official tree, but a first version of the needed modifications were ready since OBSD 5.4 (see [1]). I reviewed the patches and made them compatible with the 5.7 branch (hoping that they are still applicable to -current), adding also a note into the man page. I tested them with my DELL Latitude D810 and can confirm that WOL is perfectly working, without introducing any regressions. Hope Stefan or someone else is interested in committing this code (after the necessary review, of course). All the best [1] http://marc.info/?l=openbsd-misc&m=139848956915604&w=2 --- ./brgphyreg.h.orig Sun Jan 13 06:40:05 2013 +++ ./brgphyreg.h Sun May 17 17:22:32 2015 @@ -206,6 +206,7 @@ #define BRGPHY_AUXCTL_TX_TST 0x0400 /* TX test, always 1 */ #define BRGPHY_AUXCTL_DIS_PRF 0x0080 /* dis part resp filter */ #define BRGPHY_AUXCTL_DIAG_MODE 0x0004 /* Diagnostic mode */ +#define BRGPHY_AUXCTL_WOL_ENBL 0x000A /* Enable WOL */ #define BRGPHY_MII_AUXSTS 0x19 /* AUX status */ #define BRGPHY_AUXSTS_ACOMP 0x8000 /* autoneg complete */ --- ./if_bge.c.orig Mon Feb 9 10:51:16 2015 +++ ./if_bge.c Sun May 17 17:23:20 2015 @@ -199,6 +199,10 @@ void bge_stop_fw(struct bge_softc *, int); void bge_reset(struct bge_softc *); void bge_link_upd(struct bge_softc *); +#ifndef SMALL_KERNEL +int bge_wol(struct ifnet *, int); +void bge_wol_power(struct bge_softc *); +#endif void bge_ape_lock_init(struct bge_softc *); void bge_ape_read_fw_ver(struct bge_softc *); @@ -3079,6 +3083,35 @@ CSR_WRITE_4(sc, BGE_MSI_MODE, CSR_READ_4(sc, BGE_MSI_MODE) & ~BGE_MSIMODE_ONE_SHOT_DISABLE); + #ifndef SMALL_KERNEL + if (hwcfg & BGE_HWCFG_NO_GPIO2) + sc->bge_flags |= BGE_NO_GPIO2; + + if (BGE_ASICREV(sc->bge_chipid) != BGE_ASICREV_BCM5700) { + /* Check if ASF is enabled. */ + if (!(sc->bge_flags & BGE_NO_EEPROM)) { + if (bge_read_eeprom(sc, (caddr_t)&hwcfg, + BGE_EE_FEATURE_CFG_OFFSET, sizeof(hwcfg)) == 0) { + hwcfg = ntohl(hwcfg); + if (hwcfg & BGE_HWCFG_ASF) + sc->bge_flags |= BGE_ASF_MODE; + } + } else if (hwcfg & BGE_HWCFG_ASF) { + sc->bge_flags |= BGE_ASF_MODE; + } + } + + /* Allow WoL if ASF is unsupported or disabled. */ + if (!(sc->bge_flags & BGE_ASF_MODE)) { + ifp->if_capabilities |= IFCAP_WOL; + ifp->if_wol = bge_wol; + + /* This heuristic matches the Linux driver. */ + if (!(hwcfg & BGE_HWCFG_EEPROM_WRITE_PROTECT)) + sc->bge_flags |= BGE_WOL_NEEDS_VAUX; + } + #endif + /* Hookup IRQ last. */ DPRINTFN(5, ("pci_intr_establish\n")); sc->bge_intrhand = pci_intr_establish(pc, ih, IPL_NET, bge_intr, sc, @@ -3174,11 +3207,22 @@ rv = config_activate_children(self, act); if (ifp->if_flags & IFF_RUNNING) bge_stop(sc); + #ifndef SMALL_KERNEL + bge_wol_power(sc); + #endif break; case DVACT_RESUME: if (ifp->if_flags & IFF_UP) bge_init(sc); break; + case DVACT_POWERDOWN: + rv = config_activate_children(self, act); + if (ifp->if_flags & IFF_RUNNING) + bge_stop(sc); + #ifndef SMALL_KERNEL + bge_wol_power(sc); + #endif + break; default: rv = config_activate_children(self, act); break; @@ -4783,3 +4827,177 @@ BGE_MACSTAT_CFG_CHANGED|BGE_MACSTAT_MI_COMPLETE| BGE_MACSTAT_LINK_CHANGED); } + +#ifndef SMALL_KERNEL +int +bge_wol(struct ifnet *ifp, int enable) +{ + struct bge_softc *sc = ifp->if_softc; + + if (enable) + sc->bge_flags |= BGE_WOL; + else + sc->bge_flags &= ~BGE_WOL; + + return (0); +} + +void +bge_wol_power(struct bge_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct pci_attach_args *pa = &sc->bge_pa; + pcireg_t pcireg; + int s, offset, if_flags; + u_int32_t reg; + + if (!(sc->bge_flags & BGE_WOL)) + return; + + s = splnet(); + + /* + * In case the interface was never up we need to init the + * chip for WOL to work. + * XXX Need a smaller hammer than bge_init()/bge_stop(). + */ + bge_init(sc); + + /* Tell the firmware we're taking control of WOL. */ + bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM_WOL, BGE_MAGIC_WOL_NUMBER); + DELAY(100); + + bge_stop(sc); + + /* Disable host interrupts. */ + BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR); + + /* Clear the PME status bit. */ + if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PWRMGMT, + &offset, &pcireg)) + pci_conf_write(pa->pa_pc, pa->pa_tag, offset + PCI_PMCSR, + pcireg|PCI_PMCSR_PME_STATUS); + + /* Configure 10Mbps, the chip draws too much power in D3cold. */ + if (!(sc->bge_flags & BGE_FIBER_TBI) && + !(sc->bge_flags & BGE_FIBER_MII)) { + sc->bge_ifmedia.ifm_media = IFM_ETHER|IFM_10_T; + if_flags = ifp->if_flags; + ifp->if_flags |= IFF_UP; + bge_ifmedia_upd(ifp); + ifp->if_flags = if_flags; + } + + /* Disable DMA. */ + BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_FRMHDR_DMA_ENB| + BGE_MACMODE_TXDMA_ENB|BGE_MACMODE_RXDMA_ENB); + + /* Halt CPUs. */ + BGE_SETBIT(sc, BGE_TXCPU_MODE, BGE_TXCPUMODE_HALTCPU); + BGE_SETBIT(sc, BGE_RXCPU_MODE, BGE_RXCPUMODE_HALTCPU); + + /* Configure the PHY for WOL mode. */ + bge_miibus_writereg(&sc->bge_dev, 1, BRGPHY_MII_AUXCTL, + BRGPHY_AUXCTL_WOL_ENBL); + BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_PORTMODE); + CSR_WRITE_4(sc, BGE_MAC_MODE, BGE_PORTMODE_MII| + BGE_MACMODE_LINK_POLARITY|BGE_MACMODE_MAGIC_PKT_ENB); + DELAY(100); + + /* Disable RX and TX CPUs, enable alternate clock. */ + BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_CLOCKCTL_RW); + BGE_SETBIT(sc, BGE_PCI_CLKCTL, + BGE_PCICLOCKCTL_RXCPU_CLK_DIS|BGE_PCICLOCKCTL_TXCPU_CLK_DIS| + BGE_PCICLOCKCTL_ALTCLK|BGE_PCICLOCKCTL_LOW_SPEED_PLL); + DELAY(500); + BGE_CLRBIT(sc, BGE_PCI_CLKCTL, BGE_PCICLOCKCTL_ALTCLK); + BGE_CLRBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_CLOCKCTL_RW); + + if (sc->bge_flags & BGE_WOL_NEEDS_VAUX) { + /* Switch from main power to aux power. */ + if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 || + BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5701) { + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, + BGE_MLC_INTR_ONATTN|BGE_MLC_AUTO_EEPROM| + BGE_MLC_MISCIO_OUTEN0|BGE_MLC_MISCIO_OUTEN1| + BGE_MLC_MISCIO_OUTEN2| + BGE_MLC_MISCIO_OUT0|BGE_MLC_MISCIO_OUT1); + DELAY(100); + } else if (PCI_PRODUCT(pa->pa_id) == + PCI_PRODUCT_BROADCOM_BCM5761 || + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROADCOM_BCM5761S) { + /* These have GPIO 0 and GPIO 2 swapped. */ + reg = (BGE_MLC_INTR_ONATTN|BGE_MLC_AUTO_EEPROM| + BGE_MLC_MISCIO_OUTEN0|BGE_MLC_MISCIO_OUTEN1| + BGE_MLC_MISCIO_OUTEN2| + BGE_MLC_MISCIO_OUT0|BGE_MLC_MISCIO_OUT1); + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg); + DELAY(100); + reg |= BGE_MLC_MISCIO_OUT2; + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg); + DELAY(100); + reg &= ~BGE_MLC_MISCIO_OUT0; + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg); + DELAY(100); + } else { + reg = 0; + + /* Workaround for drawing too much power. */ + if (BGE_ASICREV(sc->bge_chipid) == + BGE_ASICREV_BCM5714) { + reg |= BGE_MLC_MISCIO_OUTEN3; + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg); + DELAY(100); + } + + if (sc->bge_flags & BGE_NO_GPIO2) { + reg |= (BGE_MLC_MISCIO_OUTEN0| + BGE_MLC_MISCIO_OUTEN1| + BGE_MLC_MISCIO_OUT1); + } else { + reg |= (BGE_MLC_MISCIO_OUTEN0| + BGE_MLC_MISCIO_OUTEN1| + BGE_MLC_MISCIO_OUTEN2| + BGE_MLC_MISCIO_OUT1| + BGE_MLC_MISCIO_OUT2); + } + + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg); + DELAY(100); + + reg |= BGE_MLC_MISCIO_OUT0; + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg); + DELAY(100); + + if (!(sc->bge_flags & BGE_NO_GPIO2)) { + reg &= ~BGE_MLC_MISCIO_OUT2; + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg); + DELAY(100); + } + } + } else if (BGE_ASICREV(sc->bge_chipid) != BGE_ASICREV_BCM5700 && + BGE_ASICREV(sc->bge_chipid) != BGE_ASICREV_BCM5701) { + /* Die with vmain power. */ + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, + BGE_MLC_MISCIO_OUT1|BGE_MLC_MISCIO_OUTEN1); + DELAY(100); + BGE_CLRBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUT1); + DELAY(100); + BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUT1); + DELAY(100); + } + + /* Re-enable RX in promiscuous mode. */ + BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_ENABLE|BGE_RXMODE_RX_PROMISC); + + /* Enable PME assertion and put the device to sleep. */ + if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PWRMGMT, + &offset, ®)) { + pci_conf_write(pa->pa_pc, pa->pa_tag, offset + PCI_PMCSR, + reg|PCI_PMCSR_PME_EN); + pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D3); + } + + splx(s); +} +#endif --- ./if_bgereg.h.orig Tue Sep 2 12:14:55 2014 +++ ./if_bgereg.h Sun May 17 17:23:46 2015 @@ -77,16 +77,17 @@ #define BGE_SOFTWARE_GENCOMM_SIG 0x00000B54 #define BGE_SOFTWARE_GENCOMM_NICCFG 0x00000B58 #define BGE_SOFTWARE_GENCOMM_VER 0x00000B5C -#define BGE_VER_SHIFT 16 +#define BGE_VER_SHIFT 16 #define BGE_SOFTWARE_GENCOMM_FW 0x00000B78 -#define BGE_FW_PAUSE 0x00000002 +#define BGE_FW_PAUSE 0x00000002 +#define BGE_SOFTWARE_GENCOMM_WOL 0x00000D30 #define BGE_SOFTWARE_GENCOMM_NICCFG2 0x00000D38 #define BGE_SOFTWARE_GENCOMM_NICCFG3 0x00000D3C #define BGE_SOFTWARE_GENCOMM_NICCFG4 0x00000D60 -#define BGE_NICCFG4_GMII_MODE 0x00000002 -#define BGE_NICCFG4_RGMII_STD_IBND_DISABLE 0x00000004 -#define BGE_NICCFG4_RGMII_EXT_IBND_RX_EN 0x00000008 -#define BGE_NICCFG4_RGMII_EXT_IBND_TX_EN 0x00000010 +#define BGE_NICCFG4_GMII_MODE 0x00000002 +#define BGE_NICCFG4_RGMII_STD_IBND_DISABLE 0x00000004 +#define BGE_NICCFG4_RGMII_EXT_IBND_RX_EN 0x00000008 +#define BGE_NICCFG4_RGMII_EXT_IBND_TX_EN 0x00000010 #define BGE_SOFTWARE_GENCOMM_END 0x00000FFF #define BGE_UNMAPPED 0x00001000 #define BGE_UNMAPPED_END 0x00001FFF @@ -438,6 +439,7 @@ #define BGE_PCICLOCKCTL_PCIPLL_DISABLE 0x00004000 #define BGE_PCICLOCKCTL_SYSPLL_DISABLE 0x00008000 #define BGE_PCICLOCKCTL_BIST_ENABLE 0x00010000 +#define BGE_PCICLOCKCTL_LOW_SPEED_PLL 0x00020000 /* * High priority mailbox registers @@ -2102,6 +2104,8 @@ #define BGE_MLC_INTR_CLR 0x00000002 #define BGE_MLC_INTR_SET 0x00000004 #define BGE_MLC_INTR_ONATTN 0x00000008 +#define BGE_MLC_MISCIO_OUTEN3 0x00000040 +#define BGE_MLC_MISCIO_OUT3 0x00000080 #define BGE_MLC_MISCIO_IN0 0x00000100 #define BGE_MLC_MISCIO_IN1 0x00000200 #define BGE_MLC_MISCIO_IN2 0x00000400 @@ -2292,6 +2296,12 @@ */ #define BGE_MAGIC_NUMBER 0x4B657654 +/* + * This magic number needs to be written to the firmware mailbox at + * 0xd30 before WOL is configured. + */ +#define BGE_MAGIC_WOL_NUMBER 0x474C0000 + typedef struct { u_int32_t bge_addr_hi; u_int32_t bge_addr_lo; @@ -2481,12 +2491,15 @@ */ #define BGE_EE_MAC_OFFSET 0x7C #define BGE_EE_MAC_OFFSET_5906 0x10 +#define BGE_EE_FEATURE_CFG_OFFSET 0xC4 #define BGE_EE_HWCFG_OFFSET 0xC8 #define BGE_HWCFG_VOLTAGE 0x00000003 #define BGE_HWCFG_PHYLED_MODE 0x0000000C #define BGE_HWCFG_MEDIA 0x00000030 #define BGE_HWCFG_ASF 0x00000080 +#define BGE_HWCFG_EEPROM_WRITE_PROTECT 0x00000100 +#define BGE_HWCFG_NO_GPIO2 0x00100000 #define BGE_VOLTAGE_1POINT3 0x00000000 #define BGE_VOLTAGE_1POINT8 0x00000001 @@ -2878,6 +2891,9 @@ #define BGE_APE 0x00080000 #define BGE_CPMU_PRESENT 0x00100000 #define BGE_TAGGED_STATUS 0x00200000 +#define BGE_WOL 0x04000000 +#define BGE_WOL_NEEDS_VAUX 0x08000000 +#define BGE_NO_GPIO2 0x10000000 #define BGE_MSI 0x00400000 #define BGE_RDMA_BUG 0x00800000 #define BGE_JUMBO_RING 0x01000000 --- ./bge.4.orig Sun May 17 19:20:38 2015 +++ ./bge.4 Sun May 17 19:23:23 2015 @@ -114,6 +114,15 @@ .Pp The .Nm +driver additionally supports Wake on LAN (WoL). +See +.Xr arp 8 +and +.Xr ifconfig 8 +for more details. +.Pp +The +.Nm driver supports the following media types: .Bl -tag -width 1000baseSX .It Cm autoselect -- Alessandro DE LAURENZIS [mailto:just22....@gmail.com] LinkedIn: http://it.linkedin.com/in/delaurenzis