On Wed, Apr 23, 2014 at 03:16:52PM +0200, Alessandro DE LAURENZIS wrote:
> Your patch is still applicable to 5.4 stable (with only minor refinements),
> but unfortunately it doesn't change anything for this BGE chip...

Thanks, this motivated me to poke a bit further at it.

Just to make sure: Did you try to enable wol with 'ifconfig bge0 wol'?

> Do you think a PCMCIA Eth card with one of the mentioned chips could be
> a reasonable solution? I mean, my understanding is that the BIOS plays a
> role in WoL stuff, so I'm wondering if a PCMCIA will stay powered on
> even when the system is switched off, like the Ethernet port is supposed
> to be...

I doubt PCMCIA would work. Cards going into PCI slots should work.
Some have a small power cable coming off the NIC that can be plugged
into the mainboard. Newer cards use the PCI bus directly for this
via the PME# signal.

Anyway, I've revised my bge diff a bit. It first checks if ASF is disabled
and allows WOL if it is disabled. We really don't want to trust ASF.
http://www.intel.com/content/dam/doc/white-paper/asf-standards-based-systems-management-paper.pdf
It's a component of AMT. AMT has pretty bad security:
http://web.it.kth.se/~maguire/DEGREE-PROJECT-REPORTS/100402-Vassilios_Ververis-with-cover.pdf

Not sure if we can trust the EEPROM not to tell lies about ASF
being disabled. But that can be discussed with others once I know
whether this diff makes wol work for you. I hope I got the power-down
sequnce right for all cards, it's based on what Linux does but
I don't have any hardware to test this.

Diff is against -current from a week ago or so. Might work with 5.5.

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