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