git: fd9a9cbcdf27 - stable/15 - igc: Disable PCIe L1.2 on I225

Kevin Bowling <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a767a89.3eba2.257fbb66__46620.4151323595$1786149666$gmane$org@gitrepo.freebsd.org>
The branch stable/15 has been updated by kbowling:

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

commit fd9a9cbcdf27e8dabec9c4ce3edf4c4f868f0cf2
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-03 09:55:15 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-08-08 00:37:45 +0000

    igc: Disable PCIe L1.2 on I225
    
    I225 devices can incorrectly enter L1 substates while CLKREQ# is
    asserted, both while idle and in D3.  Disable ASPM and PCI-PM L1.2 on
    I225 to prevent the resulting packet loss.
    
    Keep the I226 workaround ASPM-only because it addresses a separate
    traffic exit latency observation.
    
    PR:             265714
    
    (cherry picked from commit 4a28d390f5fbae2483e88805559881b04ccf9a80)
---
 sys/dev/igc/if_igc.c   | 42 ++++++++++++++++++++++--------------------
 sys/dev/igc/igc_base.c | 29 +++++++++++++++++++++++++++++
 sys/dev/igc/igc_base.h |  1 +
 3 files changed, 52 insertions(+), 20 deletions(-)

diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c
index c015bf1f3c52..fe278f2029f3 100644
--- a/sys/dev/igc/if_igc.c
+++ b/sys/dev/igc/if_igc.c
@@ -115,7 +115,7 @@ static bool	igc_if_needs_restart(if_ctx_t, enum iflib_restart_event);
 static void	igc_identify_hardware(if_ctx_t);
 static int	igc_allocate_pci_resources(if_ctx_t);
 static void	igc_free_pci_resources(if_ctx_t);
-static void	igc_disable_broken_aspm_l1_2(if_ctx_t);
+static void	igc_disable_broken_l1_2(if_ctx_t);
 static void	igc_reset(if_ctx_t);
 static int	igc_setup_interface(if_ctx_t);
 static int	igc_setup_msix(if_ctx_t);
@@ -556,13 +556,8 @@ igc_if_attach_pre(if_ctx_t ctx)
 	/* Determine hardware and mac info */
 	igc_identify_hardware(ctx);
 
-	/*
-	 * I226 parts have an erratum where the PCIe ASPM L1.2 exit
-	 * latency can exceed what the packet buffer can absorb under
-	 * load, stalling the inbound packet stream.  Disable ASPM L1.2
-	 * on the device to work around it.
-	 */
-	igc_disable_broken_aspm_l1_2(ctx);
+	/* Apply device-specific PCIe L1.2 errata workarounds. */
+	igc_disable_broken_l1_2(ctx);
 
 	scctx->isc_tx_nsegments = IGC_MAX_SCATTER;
 	scctx->isc_nrxqsets_max =
@@ -807,10 +802,10 @@ static int
 igc_if_resume(if_ctx_t ctx)
 {
 	/*
-	 * PCIe config space, and with it ASPM L1.2, may have been reset
+	 * PCIe config space, and with it L1.2, may have been reset
 	 * across the suspend/resume cycle.
 	 */
-	igc_disable_broken_aspm_l1_2(ctx);
+	igc_disable_broken_l1_2(ctx);
 
 	igc_if_init(ctx);
 
@@ -1515,30 +1510,37 @@ igc_identify_hardware(if_ctx_t ctx)
 
 /*********************************************************************
  *
- *  I226 devices advertise support for the PCIe L1.2 link substate, but
- *  due to a hardware erratum the exit latency from that low-power state
- *  can exceed what the packet buffer can tolerate under load, which
- *  stalls the inbound packet stream.  Disabling ASPM L1.2 on the device
- *  itself (as opposed to disabling ASPM/power management in the BIOS or
- *  at the OS level) works around the issue.
+ *  Intel's I225/I226 Specification Update, erratum 2, states that I225
+ *  devices can incorrectly enter L1 substates while CLKREQ# is asserted,
+ *  causing repeated L1-substate entry and exit.  Disable both ASPM and
+ *  PCI-PM L1.2, as the erratum can occur while idle or in D3.
+ *
+ *  I226 devices have a separate erratum where ASPM L1.2 exit latency can
+ *  exceed what the packet buffer can tolerate under load.  Disabling ASPM
+ *  L1.2 on the device itself works around the issue.
  *
  **********************************************************************/
 static void
-igc_disable_broken_aspm_l1_2(if_ctx_t ctx)
+igc_disable_broken_l1_2(if_ctx_t ctx)
 {
 	device_t dev = iflib_get_dev(ctx);
 	struct igc_softc *sc = iflib_get_softc(ctx);
 	int cap;
-	uint32_t ctl1;
+	uint32_t ctl1, mask;
 
-	if (!igc_is_device_id_i226(&sc->hw))
+	if (igc_is_device_id_i225(&sc->hw))
+		mask = PCIM_L1PM_CTL1_ASPM_L1_2 |
+		    PCIM_L1PM_CTL1_PCIPM_L1_2;
+	else if (igc_is_device_id_i226(&sc->hw))
+		mask = PCIM_L1PM_CTL1_ASPM_L1_2;
+	else
 		return;
 
 	if (pci_find_extcap(dev, PCIZ_L1PM, &cap) != 0)
 		return;
 
 	ctl1 = pci_read_config(dev, cap + PCIR_L1PM_CTL1, 4);
-	ctl1 &= ~PCIM_L1PM_CTL1_ASPM_L1_2;
+	ctl1 &= ~mask;
 	pci_write_config(dev, cap + PCIR_L1PM_CTL1, ctl1, 4);
 }
 
diff --git a/sys/dev/igc/igc_base.c b/sys/dev/igc/igc_base.c
index e3ab9733009f..510f4c132b9a 100644
--- a/sys/dev/igc/igc_base.c
+++ b/sys/dev/igc/igc_base.c
@@ -185,6 +185,35 @@ void igc_rx_fifo_flush_base(struct igc_hw *hw)
 	IGC_READ_REG(hw, IGC_MPC);
 }
 
+/**
+ *  igc_is_device_id_i225 - Check whether the device is I225 silicon
+ *  @hw: pointer to the HW structure
+ *
+ *  I225 and I226 share the same mac.type, so this checks the PCI
+ *  device ID directly to distinguish I225 parts, e.g. for erratum
+ *  workarounds that apply only to that silicon.
+ *
+ *  I225_BLANK_NVM is absent from the equivalent Linux helper.  I220 is
+ *  kept separate because Intel's I225 specification update does not
+ *  identify it as affected.
+ **/
+bool igc_is_device_id_i225(struct igc_hw *hw)
+{
+	switch (hw->device_id) {
+	case IGC_DEV_ID_I225_LM:
+	case IGC_DEV_ID_I225_V:
+	case IGC_DEV_ID_I225_K:
+	case IGC_DEV_ID_I225_I:
+	case IGC_DEV_ID_I225_K2:
+	case IGC_DEV_ID_I225_LMVP:
+	case IGC_DEV_ID_I225_IT:
+	case IGC_DEV_ID_I225_BLANK_NVM:
+		return true;
+	default:
+		return false;
+	}
+}
+
 /**
  *  igc_is_device_id_i226 - Check whether the device is I226 silicon
  *  @hw: pointer to the HW structure
diff --git a/sys/dev/igc/igc_base.h b/sys/dev/igc/igc_base.h
index 91cb602f809d..26344330ffed 100644
--- a/sys/dev/igc/igc_base.h
+++ b/sys/dev/igc/igc_base.h
@@ -13,6 +13,7 @@ void igc_power_down_phy_copper_base(struct igc_hw *hw);
 extern void igc_rx_fifo_flush_base(struct igc_hw *hw);
 s32 igc_acquire_phy_base(struct igc_hw *hw);
 void igc_release_phy_base(struct igc_hw *hw);
+bool igc_is_device_id_i225(struct igc_hw *hw);
 bool igc_is_device_id_i226(struct igc_hw *hw);
 
 /* Transmit Descriptor - Advanced */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.