This is an attempt to add wol support to xl(4).

Unfortunately, while I have an xl(4) card to test with none of the
motherboards I have will do WOL with it since they all lack an
on-board WOL connector :(

So test reports are needed.
Please also check whether WOL is disabled by default.

Index: ic/xl.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/xl.c,v
retrieving revision 1.99
diff -u -p -r1.99 xl.c
--- ic/xl.c     22 Sep 2010 08:49:14 -0000      1.99
+++ ic/xl.c     31 Mar 2011 15:48:36 -0000
@@ -191,6 +191,9 @@ void xl_testpacket(struct xl_softc *);
 int xl_miibus_readreg(struct device *, int, int);
 void xl_miibus_writereg(struct device *, int, int, int);
 void xl_miibus_statchg(struct device *);
+#ifndef SMALL_KERNEL
+int xl_wol(struct ifnet *, int);
+#endif
 
 int
 xl_activate(struct device *self, int act)
@@ -2368,6 +2371,12 @@ xl_stop(struct xl_softc *sc)
        ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
 
        xl_freetxrx(sc);
+
+#ifndef SMALL_KERNEL
+       /* Call upper layer WOL power routine if WOL is enabled. */
+       if ((sc->xl_flags & XL_FLAG_WOL) && sc->wol_power)
+               sc->wol_power(sc->wol_power_arg);
+#endif
 }
 
 void
@@ -2637,6 +2646,15 @@ xl_attach(struct xl_softc *sc)
                CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS);
        }
 
+#ifndef SMALL_KERNEL
+       /* Check availability of WOL. */
+       if ((sc->xl_caps & XL_CAPS_PWRMGMT) != 0) {
+               ifp->if_capabilities |= IFCAP_WOL;
+               ifp->if_wol = xl_wol;
+               xl_wol(ifp, 0);
+       }
+#endif
+
        /*
         * Call MI attach routines.
         */
@@ -2668,6 +2686,24 @@ xl_detach(struct xl_softc *sc)
 
        return (0);
 }
+
+#ifndef SMALL_KERNEL
+int
+xl_wol(struct ifnet *ifp, int enable)
+{
+       struct xl_softc         *sc = ifp->if_softc;
+
+       XL_SEL_WIN(7);
+       if (enable) {
+               CSR_WRITE_2(sc, XL_W7_BM_PME, XL_BM_PME_MAGIC);
+               sc->xl_flags |= XL_FLAG_WOL;
+       } else {
+               CSR_WRITE_2(sc, XL_W7_BM_PME, 0);
+               sc->xl_flags &= ~XL_FLAG_WOL;
+       }
+       return (0);     
+}
+#endif
 
 struct cfdriver xl_cd = {
        0, "xl", DV_IFNET
Index: ic/xlreg.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/xlreg.h,v
retrieving revision 1.26
diff -u -p -r1.26 xlreg.h
--- ic/xlreg.h  21 Sep 2010 01:05:12 -0000      1.26
+++ ic/xlreg.h  31 Mar 2011 15:42:36 -0000
@@ -411,6 +411,12 @@
 #define XL_W7_BM_LEN           0x06
 #define XL_W7_BM_STATUS                0x0B
 #define XL_W7_BM_TIMEr         0x0A
+#define XL_W7_BM_PME           0x0C
+
+#define        XL_BM_PME_WAKE          0x0001
+#define        XL_BM_PME_MAGIC         0x0002
+#define        XL_BM_PME_LINKCHG       0x0004
+#define        XL_BM_PME_WAKETIMER     0x0008
 
 /*
  * bus master control registers
@@ -571,6 +577,7 @@ struct xl_mii_frame {
 #define XL_FLAG_NO_XCVR_PWR            0x0080
 #define XL_FLAG_USE_MMIO               0x0100
 #define XL_FLAG_NO_MMIO                        0x0200
+#define XL_FLAG_WOL                    0x0400
 
 #define XL_NO_XCVR_PWR_MAGICBITS       0x0900
 
@@ -604,6 +611,8 @@ struct xl_softc {
        caddr_t                 sc_listkva;
        bus_dmamap_t            sc_rx_sparemap;
        bus_dmamap_t            sc_tx_sparemap;
+       void (*wol_power)(void *);
+       void *wol_power_arg;
 };
 
 #define xl_rx_goodframes(x) \
@@ -740,6 +749,13 @@ struct xl_stats {
 #define XL_PSTATE_D3           0x0003
 #define XL_PME_EN              0x0010
 #define XL_PME_STATUS          0x8000
+
+/* Bits in the XL_PCI_PWRMGMTCAP register */
+#define XL_PME_CAP_D0          0x0800
+#define XL_PME_CAP_D1          0x1000
+#define XL_PME_CAP_D2          0x2000
+#define XL_PME_CAP_D3_HOT      0x4000
+#define XL_PME_CAP_D3_COLD     0x8000
 
 extern int xl_intr(void *);
 extern void xl_attach(struct xl_softc *);
Index: pci/if_xl_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_xl_pci.c,v
retrieving revision 1.34
diff -u -p -r1.34 if_xl_pci.c
--- pci/if_xl_pci.c     19 Sep 2010 09:22:58 -0000      1.34
+++ pci/if_xl_pci.c     31 Mar 2011 15:43:05 -0000
@@ -92,10 +92,14 @@ int xl_pci_match(struct device *, void *
 void xl_pci_attach(struct device *, struct device *, void *);
 int xl_pci_detach(struct device *, int);
 void xl_pci_intr_ack(struct xl_softc *);
+#ifndef SMALL_KERNEL
+void xl_pci_wol_power(void *);
+#endif
 
 struct xl_pci_softc {
        struct xl_softc         psc_softc;
        pci_chipset_tag_t       psc_pc;
+       pcitag_t                psc_tag;
        bus_size_t              psc_iosize;
        bus_size_t              psc_funsize;
 };
@@ -156,9 +160,11 @@ xl_pci_attach(struct device *parent, str
        u_int32_t command;
 
        psc->psc_pc = pc;
+       psc->psc_tag = pa->pa_tag;
        sc->sc_dmat = pa->pa_dmat;
 
        sc->xl_flags = 0;
+       sc->wol_power = sc->wol_power_arg = NULL;
 
        /* set required flags */
        switch (PCI_PRODUCT(pa->pa_id)) {
@@ -260,6 +266,18 @@ xl_pci_attach(struct device *parent, str
                        pci_conf_write(pc, pa->pa_tag, XL_PCI_LOMEM, mem);
                        pci_conf_write(pc, pa->pa_tag, XL_PCI_INTLINE, irq);
                }
+
+#ifndef SMALL_KERNEL
+               /* The card is WOL-capable if it supports PME# assertion
+                * from D3hot power state. Install a callback to configure
+                * PCI power state for WOL. It will be invoked when the
+                * interface stops and WOL was enabled. */
+               command = pci_conf_read(pc, pa->pa_tag, XL_PCI_PWRMGMTCAP);
+               if (command & XL_PME_CAP_D3_HOT) {
+                       sc->wol_power = xl_pci_wol_power;
+                       sc->wol_power_arg = psc; 
+               }
+#endif
        }
 
        /*
@@ -342,3 +360,18 @@ xl_pci_intr_ack(struct xl_softc *sc)
        bus_space_write_4(sc->xl_funct, sc->xl_funch, XL_PCI_INTR,
            XL_PCI_INTRACK);
 }
+
+#ifndef SMALL_KERNEL
+void
+xl_pci_wol_power(void *ppsc)
+{
+       u_int32_t       command;
+       struct xl_pci_softc *psc = (struct xl_pci_softc*)ppsc;
+
+       /* Make sure power management is enabled, and set the card into
+        * D3hot power state so it stays active after system shutdown. */
+       command = pci_conf_read(psc->psc_pc, psc->psc_tag, XL_PCI_PWRMGMTCTRL);
+       command |= XL_PME_EN | XL_PSTATE_D3;
+       pci_conf_write(psc->psc_pc, psc->psc_tag, XL_PCI_PWRMGMTCTRL, command);
+}
+#endif

Reply via email to