git: 5b4a7fd9378a - main - e1000: Handle I354 internal memory errors

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Sat, 29 Aug 2026 02:02:53 UTC
The branch main has been updated by kbowling:

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

commit 5b4a7fd9378abba882a87330af31351c6353aea7
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-28 12:37:35 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-29 02:01:59 +0000

    e1000: Handle I354 internal memory errors
    
    The Atom C2000 integrated GbE programming reference documents the I354
    internal memory error architecture.  It shares the I350 PEIND and
    ICR.FER routing, DMA and packet-buffer status, LAN parity status, and
    required reset recovery.
    
    Extend the existing I350 recovery and corrected error accounting paths
    to I354.  Keep the PCIe corrected error mask family-specific.  C2000
    PCIEECCSTS ends at the transmit write-data indication in bit 4 and does
    not implement the I350 retry buffer indication in bit 5.  Do not expose
    the corresponding retry counter on I354.
    
    The PRM overview says a PCIe region failure requires a system reboot,
    while the individual PCIEERRSTS fields prescribe CTRL.RST followed by
    port reinitialization.  Use the register specific recovery, matching the
    existing I350 path; failed reinitialization still leaves the port down.
    
    This follows sections 5.6 and 6.21 of the Intel Atom Processor C2000
    Product Family Integrated GbE Controller Programmer's Reference Manual,
    document 537426 revision 1.5.
    
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
---
 sys/dev/e1000/e1000_defines.h | 18 ++++++-----
 sys/dev/e1000/if_em.c         | 75 +++++++++++++++++++++++--------------------
 2 files changed, 50 insertions(+), 43 deletions(-)

diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h
index 371fee155dd0..21f54e15b9a7 100644
--- a/sys/dev/e1000/e1000_defines.h
+++ b/sys/dev/e1000/e1000_defines.h
@@ -535,7 +535,7 @@
 #define E1000_ECC_82575_UNCORR_CNT_SHIFT	8
 #define E1000_ECC_82575_ENABLE			0x00010000
 
-/* I350 and I210/I211 memory error status bits. */
+/* I350, I354, and I210/I211 memory error status bits. */
 #define E1000_PEIND_LANPORT_PARITY_FATAL	0x00000001
 #define E1000_PEIND_MNG_PARITY_FATAL	0x00000002
 #define E1000_PEIND_PCIE_PARITY_FATAL	0x00000004
@@ -572,12 +572,14 @@
 #define E1000_PBECCSTS_I210_CORR_ERR	0x00000004
 
 #define E1000_PCIEERRSTS_I210_FATAL_MASK	0x00000078
-#define E1000_PCIEERRSTS_I350_FATAL_MASK	0x0000007C
+#define E1000_PCIEERRSTS_I350_I354_FATAL_MASK	0x0000007C
 #define E1000_PCIEECCSTS_TX_WR_DATA	0x00000010
 #define E1000_PCIEECCSTS_RETRY_BUF	0x00000020
 #define E1000_PCIEECCSTS_I210_CORR_MASK	0x00000030
-#define E1000_PCIEECCSTS_I350_OTHER_MASK	0x0000000F
+#define E1000_PCIEECCSTS_I350_I354_OTHER_MASK	0x0000000F
 #define E1000_PCIEECCSTS_I350_CORR_MASK	0x0000003F
+/* I354 has no I350-style PCIe retry-buffer status bit. */
+#define E1000_PCIEECCSTS_I354_CORR_MASK	0x0000001F
 
 #define E1000_DTPARS_CORR_MASK		0x0000005B
 #define E1000_DTPARS_FATAL_MASK		0x00000020
@@ -585,13 +587,13 @@
 #define E1000_DRPARS_FATAL_MASK		0x00000002
 #define E1000_DDECCS_CORR_MASK		0x0000000F
 
-#define E1000_PBECCSTS_I350_ENABLE_MASK	0x00030000
-#define E1000_PBECCSTS_I350_CORR_MASK	0x14000000
+#define E1000_PBECCSTS_I350_I354_ENABLE_MASK	0x00030000
+#define E1000_PBECCSTS_I350_I354_CORR_MASK	0x14000000
 
 #define E1000_LANPERRSTS_RETX_BUF		0x00000200
-#define E1000_LANPERRSTS_I350_NO_RESET_MASK	0x00008400
-#define E1000_LANPERRSTS_I350_RESET_MASK	0x00007BFE
-#define E1000_LANPERRSTS_I350_FATAL_MASK	0x0000FFFE
+#define E1000_LANPERRSTS_I350_I354_NO_RESET_MASK	0x00008400
+#define E1000_LANPERRSTS_I350_I354_RESET_MASK	0x00007BFE
+#define E1000_LANPERRSTS_I350_I354_FATAL_MASK	0x0000FFFE
 
 #define IFS_MAX			80
 #define IFS_MIN			40
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 082549767315..de1b513f1040 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -2360,10 +2360,11 @@ em_has_i210_memory_errors(const struct e1000_hw *hw)
 }
 
 static bool
-em_has_i350_memory_errors(const struct e1000_hw *hw)
+em_has_i350_i354_memory_errors(const struct e1000_hw *hw)
 {
 
-	return (hw->mac.type == e1000_i350);
+	return (hw->mac.type == e1000_i350 ||
+	    hw->mac.type == e1000_i354);
 }
 
 static void
@@ -2373,7 +2374,7 @@ em_configure_peind_memory_errors(struct e1000_softc *sc)
 	u32 peindm;
 
 	hw = &sc->hw;
-	if (!em_has_i350_memory_errors(hw) &&
+	if (!em_has_i350_i354_memory_errors(hw) &&
 	    !em_has_i210_memory_errors(hw))
 		return;
 
@@ -2391,7 +2392,7 @@ em_has_peind_memory_errors(const struct e1000_hw *hw)
 {
 
 	return (em_has_82580_memory_errors(hw) ||
-	    em_has_i350_memory_errors(hw) ||
+	    em_has_i350_i354_memory_errors(hw) ||
 	    em_has_i210_memory_errors(hw));
 }
 
@@ -2401,8 +2402,8 @@ em_pcie_fatal_error_mask(const struct e1000_hw *hw)
 
 	if (em_has_82580_memory_errors(hw))
 		return (~0U);
-	if (em_has_i350_memory_errors(hw))
-		return (E1000_PCIEERRSTS_I350_FATAL_MASK);
+	if (em_has_i350_i354_memory_errors(hw))
+		return (E1000_PCIEERRSTS_I350_I354_FATAL_MASK);
 	if (em_has_i210_memory_errors(hw))
 		return (E1000_PCIEERRSTS_I210_FATAL_MASK);
 	return (0);
@@ -2589,10 +2590,10 @@ em_update_i210_ecc_stats(struct e1000_softc *sc)
 }
 
 static void
-em_update_i350_ecc_stats(struct e1000_softc *sc)
+em_update_i350_i354_ecc_stats(struct e1000_softc *sc)
 {
 	struct e1000_hw *hw;
-	u32 pbeccsts, status;
+	u32 pbeccsts, pcieecc_mask, status;
 
 	hw = &sc->hw;
 	status = E1000_READ_REG(hw, E1000_DTPARS) &
@@ -2615,31 +2616,33 @@ em_update_i350_ecc_stats(struct e1000_softc *sc)
 	}
 
 	pbeccsts = E1000_READ_REG(hw, E1000_RPBECCSTS);
-	status = pbeccsts & E1000_PBECCSTS_I350_CORR_MASK;
+	status = pbeccsts & E1000_PBECCSTS_I350_I354_CORR_MASK;
 	if (status != 0) {
 		sc->corrected_error_packet_buffer_count += bitcount32(status);
 		/* Preserve the enable bits while clearing RW1C status. */
 		E1000_WRITE_REG(hw, E1000_RPBECCSTS,
-		    pbeccsts & (E1000_PBECCSTS_I350_ENABLE_MASK |
-		    E1000_PBECCSTS_I350_CORR_MASK));
+		    pbeccsts & (E1000_PBECCSTS_I350_I354_ENABLE_MASK |
+		    E1000_PBECCSTS_I350_I354_CORR_MASK));
 	}
 	pbeccsts = E1000_READ_REG(hw, E1000_TPBECCSTS);
-	status = pbeccsts & E1000_PBECCSTS_I350_CORR_MASK;
+	status = pbeccsts & E1000_PBECCSTS_I350_I354_CORR_MASK;
 	if (status != 0) {
 		sc->corrected_error_packet_buffer_count += bitcount32(status);
 		E1000_WRITE_REG(hw, E1000_TPBECCSTS,
-		    pbeccsts & (E1000_PBECCSTS_I350_ENABLE_MASK |
-		    E1000_PBECCSTS_I350_CORR_MASK));
+		    pbeccsts & (E1000_PBECCSTS_I350_I354_ENABLE_MASK |
+		    E1000_PBECCSTS_I350_I354_CORR_MASK));
 	}
 
-	status = E1000_READ_REG(hw, E1000_PCIEECCSTS) &
+	pcieecc_mask = hw->mac.type == e1000_i354 ?
+	    E1000_PCIEECCSTS_I354_CORR_MASK :
 	    E1000_PCIEECCSTS_I350_CORR_MASK;
+	status = E1000_READ_REG(hw, E1000_PCIEECCSTS) & pcieecc_mask;
 	if (status & E1000_PCIEECCSTS_TX_WR_DATA)
 		sc->corrected_error_pcie_tx_data_count++;
 	if (status & E1000_PCIEECCSTS_RETRY_BUF)
 		sc->corrected_error_pcie_retry_count++;
 	sc->corrected_error_pcie_other_count += bitcount32(status &
-	    E1000_PCIEECCSTS_I350_OTHER_MASK);
+	    E1000_PCIEECCSTS_I350_I354_OTHER_MASK);
 	if (status != 0)
 		E1000_WRITE_REG(hw, E1000_PCIEECCSTS, status);
 }
@@ -2698,13 +2701,13 @@ em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
 			    E1000_DDPARS_82580);
 			lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
 			    E1000_LANPERRSTS_82580_ERROR_MASK;
-		} else if (em_has_i350_memory_errors(hw)) {
+		} else if (em_has_i350_i354_memory_errors(hw)) {
 			dma_tx = E1000_READ_REG(hw, E1000_DTPARS) &
 			    E1000_DTPARS_FATAL_MASK;
 			dma_rx = E1000_READ_REG(hw, E1000_DRPARS) &
 			    E1000_DRPARS_FATAL_MASK;
 			lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
-			    E1000_LANPERRSTS_I350_FATAL_MASK;
+			    E1000_LANPERRSTS_I350_I354_FATAL_MASK;
 		} else {
 			dma_tx = 0;
 			dma_rx = 0;
@@ -2836,19 +2839,19 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
 		if (peind == 0)
 			reset_required = true;
 		if (peind & E1000_PEIND_LANPORT_PARITY_FATAL) {
-			if (!em_has_i350_memory_errors(&sc->hw) ||
+			if (!em_has_i350_i354_memory_errors(&sc->hw) ||
 			    sc->fatal_error_lan == 0 ||
 			    (sc->fatal_error_lan &
-			    E1000_LANPERRSTS_I350_RESET_MASK) != 0)
+			    E1000_LANPERRSTS_I350_I354_RESET_MASK) != 0)
 				reset_required = true;
 		}
 		/* Management-memory recovery belongs to management firmware. */
 		if (!reset_required) {
-			if (em_has_i350_memory_errors(&sc->hw) &&
+			if (em_has_i350_i354_memory_errors(&sc->hw) &&
 			    sc->fatal_error_lan != 0)
 				E1000_WRITE_REG(&sc->hw, E1000_LANPERRSTS,
 				    sc->fatal_error_lan &
-				    E1000_LANPERRSTS_I350_NO_RESET_MASK);
+				    E1000_LANPERRSTS_I350_I354_NO_RESET_MASK);
 			sc->fatal_error_peind = 0;
 			sc->fatal_error_pcie = 0;
 			sc->fatal_error_pcie_ecc = 0;
@@ -2874,11 +2877,11 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
 }
 
 /*
- * A PCIe-region parity failure stops PCIe and DMA traffic.  I350, I210, and
- * I211 require a port reset before master disable in this case.  82580 stops
- * PCIe traffic for a fatal error in any host-owned region, so use the same
- * order for every 82580 recovery.  This differs from the normal reset path,
- * which disables the bus master first.
+ * A PCIe-region parity failure stops PCIe and DMA traffic.  I350, I354,
+ * I210, and I211 require a port reset before master disable in this case.
+ * 82580 stops PCIe traffic for a fatal error in any host-owned region, so use
+ * the same order for every 82580 recovery.  This differs from the normal
+ * reset path, which disables the bus master first.
  *
  * Indications that relatch after admin accounting are discarded during
  * reset; sticky bits cannot distinguish them from the saved event.
@@ -3004,7 +3007,7 @@ em_finish_fatal_error_reset(struct e1000_softc *sc)
 		    em_pcie_fatal_error_mask(hw));
 		if (pcieerr != 0)
 			E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
-		if (em_has_i350_memory_errors(hw)) {
+		if (em_has_i350_i354_memory_errors(hw)) {
 			dma_tx = sc->fatal_error_dma_tx |
 			    (E1000_READ_REG(hw, E1000_DTPARS) &
 			    E1000_DTPARS_FATAL_MASK);
@@ -3017,7 +3020,7 @@ em_finish_fatal_error_reset(struct e1000_softc *sc)
 				E1000_WRITE_REG(hw, E1000_DRPARS, dma_rx);
 			lanerr = sc->fatal_error_lan |
 			    (E1000_READ_REG(hw, E1000_LANPERRSTS) &
-			    E1000_LANPERRSTS_I350_FATAL_MASK);
+			    E1000_LANPERRSTS_I350_I354_FATAL_MASK);
 		} else {
 			lanerr = sc->fatal_error_lan |
 			    (E1000_READ_REG(hw, E1000_LANPERRSTS) &
@@ -6631,8 +6634,8 @@ em_update_stats_counters(struct e1000_softc *sc)
 		    E1000_READ_REG(&sc->hw, E1000_RPBECCSTS),
 		    E1000_READ_REG(&sc->hw, E1000_TPBECCSTS),
 		    E1000_READ_REG(&sc->hw, E1000_PCIEECCSTS));
-	else if (em_has_i350_memory_errors(&sc->hw))
-		em_update_i350_ecc_stats(sc);
+	else if (em_has_i350_i354_memory_errors(&sc->hw))
+		em_update_i350_i354_ecc_stats(sc);
 	else if (em_has_i210_memory_errors(&sc->hw))
 		em_update_i210_ecc_stats(sc);
 }
@@ -7180,10 +7183,12 @@ em_add_hw_stats(struct e1000_softc *sc)
 				    "corrected_pcie_tx_data", CTLFLAG_RD,
 				    &sc->corrected_error_pcie_tx_data_count,
 				    "Corrected PCIe transmit-data memory indications");
-				SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
-				    "corrected_pcie_retry", CTLFLAG_RD,
-				    &sc->corrected_error_pcie_retry_count,
-				    "Corrected PCIe retry-buffer memory indications");
+				if (sc->hw.mac.type == e1000_i350)
+					SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+					    "corrected_pcie_retry", CTLFLAG_RD,
+					    &sc->corrected_error_pcie_retry_count,
+					    "Corrected PCIe retry-buffer memory "
+					    "indications");
 				SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
 				    "corrected_pcie_other", CTLFLAG_RD,
 				    &sc->corrected_error_pcie_other_count,