git: 4a28d390f5fb - main - igc: Disable PCIe L1.2 on I225

Kevin Bowling <[email protected]> Mon, 03 Aug 2026 10:00:08 +0000
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a7066a8.409e7.3c078c31__20210.6421392941$1785751234$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by kbowling:

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

commit 4a28d390f5fbae2483e88805559881b04ccf9a80
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-03 09:55:15 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-08-03 10:00:00 +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
    MFC after:      4 days
---
 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 f8de961afeca..4a54d8ca7a6c 100644
--- a/sys/dev/igc/if_igc.c
+++ b/sys/dev/igc/if_igc.c
@@ -114,7 +114,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);
@@ -555,13 +555,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 =
@@ -806,10 +801,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);
 
@@ -1514,30 +1509,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 */