git: fc68534a9ad9 - main - rge: add Wake-on-LAN support for magic packet

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Fri, 10 Apr 2026 17:43:17 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=fc68534a9ad93f6df1756ffa8e707c30a35ce4d7

commit fc68534a9ad93f6df1756ffa8e707c30a35ce4d7
Author:     Christos Longros <chris.longros@gmail.com>
AuthorDate: 2026-04-10 17:31:38 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-04-10 17:31:38 +0000

    rge: add Wake-on-LAN support for magic packet
    
    Advertise IFCAP_WOL_MAGIC when PCI power management is available
    and enable it by default.  On suspend or shutdown, rge_setwol()
    enables the WOL_MAGIC and WOL_LANWAKE bits in CFG3/CFG5, disables
    the RXDV gate, and enables PM so the NIC stays powered to watch
    for magic packets.
    
    Move hardware-specific WOL register configuration into
    rge_wol_config() in if_rge_hw.c to keep hardware-specific
    functions in sync with OpenBSD.
    
    Update rge.4 to document WoL support.
    
    Tested on FreeBSD 16.0-CURRENT bare metal with Realtek RTL8125
    on a Gigabyte B650 Gaming X AX motherboard.
    
    Signed-off-by: Christos Longros <chris.longros@gmail.com>
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D56259
---
 share/man/man4/rge.4    |  6 +++---
 sys/dev/rge/if_rge.c    | 57 +++++++++++++++++++++++--------------------------
 sys/dev/rge/if_rge_hw.c | 53 +++++++++++++++++----------------------------
 sys/dev/rge/if_rge_hw.h |  1 +
 4 files changed, 51 insertions(+), 66 deletions(-)

diff --git a/share/man/man4/rge.4 b/share/man/man4/rge.4
index 2b781e287e3c..f9077a56f28b 100644
--- a/share/man/man4/rge.4
+++ b/share/man/man4/rge.4
@@ -3,7 +3,7 @@
 .\"
 .\" SPDX-License-Identifier: BSD-2-Clause
 .\"
-.Dd December 18, 2025
+.Dd April 5, 2026
 .Dt RGE 4
 .Os
 .Sh NAME
@@ -39,8 +39,8 @@ over CAT6 cable.
 .Pp
 All NICs supported by the
 .Nm
-driver have TCP/IP checksum offload and hardware VLAN tagging/insertion
-features, and use a descriptor-based DMA mechanism.
+driver have TCP/IP checksum offload, hardware VLAN tagging/insertion
+features, Wake On Lan (WOL), and use a descriptor-based DMA mechanism.
 They are also
 capable of TCP large send (TCP segmentation offload).
 .Pp
diff --git a/sys/dev/rge/if_rge.c b/sys/dev/rge/if_rge.c
index 0007b07e0fa6..17225e065482 100644
--- a/sys/dev/rge/if_rge.c
+++ b/sys/dev/rge/if_rge.c
@@ -103,12 +103,7 @@ static void	rge_tx_task(void *, int);
 static void	rge_txq_flush_mbufs(struct rge_softc *sc);
 static void	rge_tick(void *);
 static void	rge_link_state(struct rge_softc *);
-#if 0
-#ifndef SMALL_KERNEL
-int		rge_wol(struct ifnet *, int);
-void		rge_wol_power(struct rge_softc *);
-#endif
-#endif
+static void	rge_setwol(struct rge_softc *);
 
 struct rge_matchid {
 	uint16_t vendor;
@@ -161,7 +156,11 @@ rge_attach_if(struct rge_softc *sc, const char *eaddr)
 	if_setcapabilities(sc->sc_ifp, IFCAP_HWCSUM);
 	if_setcapenable(sc->sc_ifp, if_getcapabilities(sc->sc_ifp));
 
-	/* TODO: set WOL */
+	/* Enable WOL if PM is supported. */
+	if (pci_has_pm(sc->sc_dev)) {
+		if_setcapabilitiesbit(sc->sc_ifp, IFCAP_WOL_MAGIC, 0);
+		if_setcapenablebit(sc->sc_ifp, IFCAP_WOL_MAGIC, 0);
+	}
 
 	/* Attach interface */
 	ether_ifattach(sc->sc_ifp, eaddr);
@@ -654,26 +653,6 @@ rge_detach(device_t dev)
 	return (0);
 }
 
-#if 0
-
-int
-rge_activate(struct device *self, int act)
-{
-#ifndef SMALL_KERNEL
-	struct rge_softc *sc = (struct rge_softc *)self;
-#endif
-
-	switch (act) {
-	case DVACT_POWERDOWN:
-#ifndef SMALL_KERNEL
-		rge_wol_power(sc);
-#endif
-		break;
-	}
-	return (0);
-}
-#endif
-
 static void
 rge_intr_msi(void *arg)
 {
@@ -1014,7 +993,9 @@ rge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 				reinit = 1;
 			}
 
-			/* TODO: WOL */
+			if ((mask & IFCAP_WOL_MAGIC) != 0 &&
+			    (if_getcapabilities(ifp) & IFCAP_WOL_MAGIC) != 0)
+				if_togglecapenable(ifp, IFCAP_WOL_MAGIC);
 
 			if ((mask & IFCAP_RXCSUM) != 0 &&
 			    (if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0) {
@@ -2620,6 +2601,22 @@ rge_link_state(struct rge_softc *sc)
 	}
 }
 
+static void
+rge_setwol(struct rge_softc *sc)
+{
+	if_t ifp = sc->sc_ifp;
+	int enable;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	if (!pci_has_pm(sc->sc_dev))
+		return;
+
+	enable = (if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0;
+
+	rge_wol_config(sc, enable);
+}
+
 /**
  * @brief Suspend
  */
@@ -2630,7 +2627,7 @@ rge_suspend(device_t dev)
 
 	RGE_LOCK(sc);
 	rge_stop_locked(sc);
-	/* TODO: wake on lan */
+	rge_setwol(sc);
 	sc->sc_suspended = true;
 	RGE_UNLOCK(sc);
 
@@ -2646,7 +2643,6 @@ rge_resume(device_t dev)
 	struct rge_softc *sc = device_get_softc(dev);
 
 	RGE_LOCK(sc);
-	/* TODO: wake on lan */
 
 	/* reinit if required */
 	if (if_getflags(sc->sc_ifp) & IFF_UP)
@@ -2669,6 +2665,7 @@ rge_shutdown(device_t dev)
 
 	RGE_LOCK(sc);
 	rge_stop_locked(sc);
+	rge_setwol(sc);
 	RGE_UNLOCK(sc);
 
 	return (0);
diff --git a/sys/dev/rge/if_rge_hw.c b/sys/dev/rge/if_rge_hw.c
index 35a0e93dd193..ba01e389af14 100644
--- a/sys/dev/rge/if_rge_hw.c
+++ b/sys/dev/rge/if_rge_hw.c
@@ -2196,50 +2196,37 @@ rge_get_link_status(struct rge_softc *sc)
 	return ((RGE_READ_2(sc, RGE_PHYSTAT) & RGE_PHYSTAT_LINK) ? 1 : 0);
 }
 
-#if 0
-#ifndef SMALL_KERNEL
-int
-rge_wol(struct ifnet *ifp, int enable)
+void
+rge_wol_config(struct rge_softc *sc, int enable)
 {
-	struct rge_softc *sc = ifp->if_softc;
-
-	if (enable) {
-		if (!(RGE_READ_1(sc, RGE_CFG1) & RGE_CFG1_PM_EN)) {
-			printf("%s: power management is disabled, "
-			    "cannot do WOL\n", sc->sc_dev.dv_xname);
-			return (ENOTSUP);
-		}
-
-	}
-
-	rge_iff(sc);
-
 	if (enable)
 		RGE_MAC_SETBIT(sc, 0xc0b6, 0x0001);
 	else
 		RGE_MAC_CLRBIT(sc, 0xc0b6, 0x0001);
 
+	/* Enable config register write. */
 	RGE_SETBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
-	RGE_CLRBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE | RGE_CFG5_WOL_UCAST |
-	    RGE_CFG5_WOL_MCAST | RGE_CFG5_WOL_BCAST);
+
+	/* Clear all WOL bits, then set as requested. */
 	RGE_CLRBIT_1(sc, RGE_CFG3, RGE_CFG3_WOL_LINK | RGE_CFG3_WOL_MAGIC);
-	if (enable)
+	RGE_CLRBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE |
+	    RGE_CFG5_WOL_UCAST | RGE_CFG5_WOL_MCAST | RGE_CFG5_WOL_BCAST);
+	if (enable) {
+		RGE_SETBIT_1(sc, RGE_CFG3, RGE_CFG3_WOL_MAGIC);
 		RGE_SETBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE);
-	RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
+	}
 
-	return (0);
-}
+	/* Config register write done. */
+	RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
 
-void
-rge_wol_power(struct rge_softc *sc)
-{
-	/* Disable RXDV gate. */
-	RGE_CLRBIT_1(sc, RGE_PPSW, 0x08);
-	DELAY(2000);
+	if (enable) {
+		/* Disable RXDV gate so WOL packets can reach the NIC. */
+		RGE_CLRBIT_1(sc, RGE_PPSW, 0x08);
+		DELAY(2000);
 
-	RGE_SETBIT_1(sc, RGE_CFG1, RGE_CFG1_PM_EN);
-	RGE_SETBIT_1(sc, RGE_CFG2, RGE_CFG2_PMSTS_EN);
+		/* Enable power management. */
+		RGE_SETBIT_1(sc, RGE_CFG1, RGE_CFG1_PM_EN);
+		RGE_SETBIT_1(sc, RGE_CFG2, RGE_CFG2_PMSTS_EN);
+	}
 }
-#endif
 
-#endif
diff --git a/sys/dev/rge/if_rge_hw.h b/sys/dev/rge/if_rge_hw.h
index 86f0da7c87b3..4e6ee5f1975f 100644
--- a/sys/dev/rge/if_rge_hw.h
+++ b/sys/dev/rge/if_rge_hw.h
@@ -37,5 +37,6 @@ extern	uint16_t rge_read_phy(struct rge_softc *, uint16_t, uint16_t);
 extern	void rge_write_phy_ocp(struct rge_softc *, uint16_t, uint16_t);
 extern	uint16_t rge_read_phy_ocp(struct rge_softc *sc, uint16_t reg);
 extern	int rge_get_link_status(struct rge_softc *);
+extern	void rge_wol_config(struct rge_softc *, int);
 
 #endif	/* __IF_RGE_HW_H__ */