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