The reason we don't enable WOL with bge cards is that they contain
ASF firmware support which should not be exposed to untrusted traffic,
so it's safer to power down bge devices altogether on power down.
Since all bges except the rare 5700 version support ASF, this currently
means no WOL support for bge cards at all.

(If you want to know what's so bad about ASF, search the net for
security problems with intel AMT -- ASF is a precursor to this.)

Apparently there is an eeprom configuration bit that tells us
if ASF is enabled or not. Can we trust this bit?
If we decide that the bit is trustworthy enough, we could allow
users to enable wol for bge cards as long as ASF is disabled
(yet I'd still want a warning in the man page).

The diff below tries to do this. I don't have any hardware to test
with so I'd be delighted if some bge owners could give this a spin.
If this doesn't make wol work and the problem can't be fixed, then
we can skip the entire ASF discussion anyway.  

To test this:

 - recompile your kernel with the below diff
 - reboot
 - run 'ifconfig bge0 wol'
 - run 'shutdown -hp now'
 - try to send a magic packet from another machine with 'arp -W MAC_ADDR'
   and hope for the bge box to power back up

If it doesn't work, please check your BIOS for WOL and ASF-related
configuration settings and check if tweaking them helps.

Thanks.

Index: mii/brgphyreg.h
===================================================================
RCS file: /cvs/src/sys/dev/mii/brgphyreg.h,v
retrieving revision 1.16
diff -u -p -r1.16 brgphyreg.h
--- mii/brgphyreg.h     13 Jan 2013 05:40:05 -0000      1.16
+++ mii/brgphyreg.h     23 Apr 2014 14:11:06 -0000
@@ -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 */
Index: pci/if_bge.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_bge.c,v
retrieving revision 1.353
diff -u -p -r1.353 if_bge.c
--- pci/if_bge.c        24 Feb 2014 20:00:48 -0000      1.353
+++ pci/if_bge.c        23 Apr 2014 15:33:54 -0000
@@ -202,6 +202,10 @@ void bge_sig_pre_reset(struct bge_softc 
 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 *);
@@ -3064,6 +3068,35 @@ bge_attach(struct device *parent, struct
        if (BGE_IS_5755_PLUS(sc) && sc->bge_flags & BGE_MSI)
                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"));
@@ -3160,6 +3193,9 @@ bge_activate(struct device *self, int ac
                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)
@@ -4728,3 +4764,177 @@ bge_link_upd(struct bge_softc *sc)
            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
Index: pci/if_bgereg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_bgereg.h,v
retrieving revision 1.123
diff -u -p -r1.123 if_bgereg.h
--- pci/if_bgereg.h     5 Feb 2014 05:59:42 -0000       1.123
+++ pci/if_bgereg.h     23 Apr 2014 15:14:07 -0000
@@ -80,6 +80,7 @@
 #define           BGE_VER_SHIFT                        16
 #define        BGE_SOFTWARE_GENCOMM_FW         0x00000B78
 #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
@@ -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;
@@ -2470,12 +2480,15 @@ struct bge_status_block {
  */
 #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
@@ -2869,6 +2882,9 @@ struct bge_softc {
 #define        BGE_TAGGED_STATUS       0x00200000
 #define        BGE_MSI                 0x00400000
 #define        BGE_RDMA_BUG            0x00800000
+#define BGE_WOL                        0x04000000
+#define BGE_WOL_NEEDS_VAUX     0x08000000
+#define BGE_NO_GPIO2           0x10000000
 
        u_int32_t               bge_phy_flags;
 #define        BGE_PHY_NO_3LED         0x00000001
Index: pci/pcireg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/pcireg.h,v
retrieving revision 1.46
diff -u -p -r1.46 pcireg.h
--- pci/pcireg.h        9 Apr 2014 06:36:33 -0000       1.46
+++ pci/pcireg.h        23 Apr 2014 14:11:06 -0000
@@ -518,11 +518,13 @@ typedef u_int8_t pci_revision_t;
  * Power Management Control Status Register; access via capability pointer.
  */
 #define PCI_PMCSR              0x04
-#define PCI_PMCSR_STATE_MASK   0x03
-#define PCI_PMCSR_STATE_D0     0x00
-#define PCI_PMCSR_STATE_D1     0x01
-#define PCI_PMCSR_STATE_D2     0x02
-#define PCI_PMCSR_STATE_D3     0x03
+#define PCI_PMCSR_STATE_MASK   0x0003
+#define PCI_PMCSR_STATE_D0     0x0000
+#define PCI_PMCSR_STATE_D1     0x0001
+#define PCI_PMCSR_STATE_D2     0x0002
+#define PCI_PMCSR_STATE_D3     0x0003
+#define PCI_PMCSR_PME_STATUS   0x8000
+#define PCI_PMCSR_PME_EN       0x0100
 
 /*
  * HyperTransport; access via capability pointer.




Reply via email to