git: 4a28d390f5fb - main - igc: Disable PCIe L1.2 on I225
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 03 Aug 2026 10:00:08 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=4a28d390f5fbae2483e88805559881b04ccf9a80
commit 4a28d390f5fbae2483e88805559881b04ccf9a80
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-03 09:55:15 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
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 */