aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Bowling <kbowling@FreeBSD.org>2026-08-03 09:55:15 +0000
committerKevin Bowling <kbowling@FreeBSD.org>2026-08-08 00:37:45 +0000
commitfd9a9cbcdf27e8dabec9c4ce3edf4c4f868f0cf2 (patch)
tree7d889d35624726ab81c6f964cde1b3f8a8d3e907
parentcf788fbe85ff7e995ee3ff535327eac62c14b3df (diff)
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)
-rw-r--r--sys/dev/igc/if_igc.c42
-rw-r--r--sys/dev/igc/igc_base.c29
-rw-r--r--sys/dev/igc/igc_base.h1
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
@@ -186,6 +186,35 @@ void igc_rx_fifo_flush_base(struct igc_hw *hw)
}
/**
+ * 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 */