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, &reg)) {
+               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

Reply via email to