git: 52fbff965499 - main - e1000: Configure PCH low-power link modes for suspend

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Mon, 31 Aug 2026 13:18:49 UTC
The branch main has been updated by kbowling:

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

commit 52fbff96549904933898908644f3a99e5cd4cdca
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-30 08:25:09 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-31 13:18:43 +0000

    e1000: Configure PCH low-power link modes for suspend
    
    The PCH suspend path kept a wake link fully powered and did not restore
    the negotiated EEE modes after its stop-time reset.  Intel provides the
    ULP entry and exit machinery in the shared code, but FreeBSD did not
    invoke its Sx policy.
    
    Enter ULP on LPT and newer PCH controllers when wake is armed without
    directed-unicast, multicast, or broadcast filters, which ULP cannot
    preserve.  For a link retained by host wake or management, restore the
    100BASE-TX and 1000BASE-T LPI controls selected by the local
    advertisement and the cached link-partner ability.
    
    Keep these power reductions best-effort: wake filters and PME are
    already configured independently, and a ULP or EEE failure is logged
    without converting an optional power optimization into a suspend
    failure.  The existing PCH resume workaround forcibly exits ULP and
    clears automatic Sx LPI state before normal initialization.
    
    Validated on a ThinkPad T440p with an I217-LM.  FBT confirmed that the
    helper received the magic-wake mask during device suspend.  The shared
    ULP helper returned its documented no-op for the initial I217 device ID.
    A full S3 cycle resumed cleanly.
    
    On a ThinkPad P51 with an I219, full S3 waited for a magic packet and
    then resumed with the 1-Gbps link and traffic restored.
    
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
---
 share/man/man4/em.4   |  5 ++++-
 sys/dev/e1000/if_em.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/share/man/man4/em.4 b/share/man/man4/em.4
index 512f0dc11dc9..c652dbbb285e 100644
--- a/share/man/man4/em.4
+++ b/share/man/man4/em.4
@@ -32,7 +32,7 @@
 .\"
 .\" * Other names and brands may be claimed as the property of others.
 .\"
-.Dd August 29, 2026
+.Dd August 30, 2026
 .Dt EM 4
 .Os
 .Sh NAME
@@ -116,6 +116,9 @@ The
 .Xr ifconfig 8
 wake capabilities select the controller packet filters; they do not enable
 the ACPI wake source.
+On supported PCH controllers, suspend uses the PHY ultra-low-power mode when
+the selected wake filters permit it and preserves negotiated Energy Efficient
+Ethernet operation while the wake link remains active.
 .Pp
 Furthermore it supports TCP segmentation offload (TSO) on all adapters but
 those based on the 82542, 82543, 82544 and 82547 controller chips.
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 19717d9e5371..e27b804a3cb4 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -509,6 +509,7 @@ static void	em_release_hw_control(struct e1000_softc *);
 static void	em_get_wakeup(if_ctx_t);
 static void	em_fill_wakeup_mta(struct e1000_hw *);
 static int	em_enable_wakeup(if_ctx_t);
+static void	em_configure_sx_low_power(struct e1000_softc *, u32);
 static int	em_enable_phy_wakeup(struct e1000_softc *, u32);
 static int	em_disable_phy_wakeup(struct e1000_softc *, u16 *);
 static void	em_power_up_wakeup_link(struct e1000_softc *);
@@ -6825,6 +6826,7 @@ em_enable_wakeup(if_ctx_t ctx)
 		if (manage) {
 			if (sc->suspend_link_powered_down)
 				em_power_up_wakeup_link(sc);
+			em_configure_sx_low_power(sc, 0);
 			pci_enable_pme(dev);
 		} else {
 			em_power_down_wakeup_link(sc);
@@ -6888,6 +6890,7 @@ em_enable_wakeup(if_ctx_t ctx)
 	if (sc->hw.mac.type < igb_mac_min &&
 	    sc->hw.phy.type == e1000_phy_igp_3)
 		e1000_igp3_phy_powerdown_workaround_ich8lan(&sc->hw);
+	em_configure_sx_low_power(sc, wufc);
 
 pme:
 	if (!error)
@@ -6913,6 +6916,63 @@ master_disable:
 	return (error == E1000_SUCCESS ? 0 : EIO);
 }
 
+/* Configure the PCH low-power link modes used while the system sleeps. */
+static void
+em_configure_sx_low_power(struct e1000_softc *sc, u32 wufc)
+{
+	struct e1000_hw *hw = &sc->hw;
+	struct e1000_dev_spec_ich8lan *dev_spec;
+	s32 error;
+	u16 eee_advert, lpi_ctrl;
+
+	if (hw->mac.type < e1000_pch_lpt || hw->mac.type >= igb_mac_min ||
+	    sc->suspend_link_powered_down)
+		return;
+
+	if (wufc != 0 &&
+	    (wufc & (E1000_WUFC_EX | E1000_WUFC_MC | E1000_WUFC_BC)) == 0) {
+		/* ULP cannot preserve directed or broad multicast wake. */
+		error = e1000_enable_ulp_lpt_lp(hw, true);
+		if (error != E1000_SUCCESS) {
+			device_printf(sc->dev,
+			    "Could not enter PHY ultra-low-power mode: %d\n",
+			    error);
+			return;
+		}
+	}
+
+	dev_spec = &hw->dev_spec.ich8lan;
+	if (hw->phy.type != e1000_phy_i217 || dev_spec->eee_disable ||
+	    dev_spec->eee_lp_ability == 0)
+		return;
+
+	error = hw->phy.ops.acquire(hw);
+	if (error != E1000_SUCCESS)
+		goto out;
+	error = hw->phy.ops.read_reg_locked(hw, I82579_LPI_CTRL, &lpi_ctrl);
+	if (error != E1000_SUCCESS)
+		goto release;
+	error = e1000_read_emi_reg_locked(hw, I217_EEE_ADVERTISEMENT,
+	    &eee_advert);
+	if (error != E1000_SUCCESS)
+		goto release;
+
+	if ((eee_advert & dev_spec->eee_lp_ability &
+	    I82579_EEE_100_SUPPORTED) != 0)
+		lpi_ctrl |= I82579_LPI_CTRL_100_ENABLE;
+	if ((eee_advert & dev_spec->eee_lp_ability &
+	    I82579_EEE_1000_SUPPORTED) != 0)
+		lpi_ctrl |= I82579_LPI_CTRL_1000_ENABLE;
+	error = hw->phy.ops.write_reg_locked(hw, I82579_LPI_CTRL, lpi_ctrl);
+release:
+	hw->phy.ops.release(hw);
+out:
+	if (error != E1000_SUCCESS)
+		device_printf(sc->dev,
+		    "Could not configure Energy Efficient Ethernet for sleep: %d\n",
+		    error);
+}
+
 static void
 em_power_up_wakeup_link(struct e1000_softc *sc)
 {