git: 262519268925 - main - e1000: Separate hardware semaphore policies by family

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Tue, 11 Aug 2026 20:10:43 UTC
The branch main has been updated by kbowling:

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

commit 26251926892585e0746c2b65227e56cf9b2fed58
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-11 18:58:17 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-11 20:08:43 +0000

    e1000: Separate hardware semaphore policies by family
    
    The shared semaphore helper accesses both the 82571 retry counter and
    the I210 one-time-clear flag.  Those fields occupy overlapping members
    of the device-specific union.  On 82571, incrementing the counter thus
    enables the I210 recovery and clears SMBI after the first timeout.
    
    Give 82571, generic 80003/82575, and I210/I211 users distinct acquire
    paths.  Preserve the legacy peer-driver policy on 82571 and one-time
    recovery on I210.
    
    The separation follows the Intel e1000 base code in DPDK.
    
    MFC after:      2 weeks
---
 sys/dev/e1000/e1000_82571.c |  62 ++++++++++++++++++++--
 sys/dev/e1000/e1000_82575.c |   5 +-
 sys/dev/e1000/e1000_i210.c  | 126 +++++++++++++++++++++++++++++++++++++++++++-
 sys/dev/e1000/e1000_i210.h  |   2 +
 sys/dev/e1000/e1000_mac.c   |  51 ++++--------------
 sys/dev/e1000/e1000_mac.h   |   2 +-
 6 files changed, 201 insertions(+), 47 deletions(-)

diff --git a/sys/dev/e1000/e1000_82571.c b/sys/dev/e1000/e1000_82571.c
index 650169663f56..a0a183e326ca 100644
--- a/sys/dev/e1000/e1000_82571.c
+++ b/sys/dev/e1000/e1000_82571.c
@@ -72,6 +72,7 @@ static s32  e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data);
 static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw);
 static s32  e1000_fix_nvm_checksum_82571(struct e1000_hw *hw);
 static s32  e1000_get_phy_id_82571(struct e1000_hw *hw);
+static s32  e1000_get_hw_semaphore_82571(struct e1000_hw *hw);
 static s32  e1000_get_hw_semaphore_82574(struct e1000_hw *hw);
 static void e1000_put_hw_semaphore_82574(struct e1000_hw *hw);
 static s32  e1000_set_d0_lplu_state_82574(struct e1000_hw *hw,
@@ -122,7 +123,7 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
 		phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
 		phy->ops.read_reg	= e1000_read_phy_reg_igp;
 		phy->ops.write_reg	= e1000_write_phy_reg_igp;
-		phy->ops.acquire	= e1000_get_hw_semaphore;
+		phy->ops.acquire	= e1000_get_hw_semaphore_82571;
 		phy->ops.release	= e1000_put_hw_semaphore;
 		break;
 	case e1000_82573:
@@ -135,7 +136,7 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
 		phy->ops.get_cable_length = e1000_get_cable_length_m88;
 		phy->ops.read_reg	= e1000_read_phy_reg_m88;
 		phy->ops.write_reg	= e1000_write_phy_reg_m88;
-		phy->ops.acquire	= e1000_get_hw_semaphore;
+		phy->ops.acquire	= e1000_get_hw_semaphore_82571;
 		phy->ops.release	= e1000_put_hw_semaphore;
 		break;
 	case e1000_82574:
@@ -615,6 +616,61 @@ static s32 e1000_set_d3_lplu_state_82574(struct e1000_hw *hw, bool active)
 	return E1000_SUCCESS;
 }
 
+/**
+ *  e1000_get_hw_semaphore_82571 - Acquire hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the hardware semaphore while preserving the 82571 inter-port
+ *  compatibility policy.
+ **/
+static s32
+e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
+{
+	u32 swsm;
+	s32 sw_timeout = hw->nvm.word_size + 1;
+	s32 fw_timeout = hw->nvm.word_size + 1;
+	s32 i = 0;
+
+	DEBUGFUNC("e1000_get_hw_semaphore_82571");
+
+	/*
+	 * After three SMBI timeouts, minimize interference with an older
+	 * peer driver which may not release the inter-port semaphore.
+	 */
+	if (hw->dev_spec._82571.smb_counter > 2)
+		sw_timeout = 1;
+
+	while (i < sw_timeout) {
+		swsm = E1000_READ_REG(hw, E1000_SWSM);
+		if (!(swsm & E1000_SWSM_SMBI))
+			break;
+		usec_delay(50);
+		i++;
+	}
+
+	if (i == sw_timeout) {
+		DEBUGOUT("Driver can't access device - SMBI bit is set.\n");
+		hw->dev_spec._82571.smb_counter++;
+	}
+
+	for (i = 0; i < fw_timeout; i++) {
+		swsm = E1000_READ_REG(hw, E1000_SWSM);
+		E1000_WRITE_REG(hw, E1000_SWSM,
+		    swsm | E1000_SWSM_SWESMBI);
+		if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI)
+			break;
+		usec_delay(50);
+	}
+
+	if (i == fw_timeout) {
+		e1000_put_hw_semaphore(hw);
+		DEBUGOUT("Driver can't access the NVM\n");
+		return -E1000_ERR_NVM;
+	}
+
+	return E1000_SUCCESS;
+}
+
 /**
  *  e1000_acquire_nvm_82571 - Request for access to the EEPROM
  *  @hw: pointer to the HW structure
@@ -630,7 +686,7 @@ static s32 e1000_acquire_nvm_82571(struct e1000_hw *hw)
 
 	DEBUGFUNC("e1000_acquire_nvm_82571");
 
-	ret_val = e1000_get_hw_semaphore(hw);
+	ret_val = e1000_get_hw_semaphore_82571(hw);
 	if (ret_val)
 		return ret_val;
 
diff --git a/sys/dev/e1000/e1000_82575.c b/sys/dev/e1000/e1000_82575.c
index d0aebbce74d8..f8b512266acf 100644
--- a/sys/dev/e1000/e1000_82575.c
+++ b/sys/dev/e1000/e1000_82575.c
@@ -426,6 +426,10 @@ static s32 e1000_init_mac_params_82575(struct e1000_hw *hw)
 	mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync;
 	/* release SW_FW sync */
 	mac->ops.release_swfw_sync = e1000_release_swfw_sync;
+	if (mac->type == e1000_i210 || mac->type == e1000_i211) {
+		mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_i210;
+		mac->ops.release_swfw_sync = e1000_release_swfw_sync_i210;
+	}
 
 	/* set lan id for port to determine which phy lock to use */
 	hw->mac.ops.set_lan_id(hw);
@@ -3532,4 +3536,3 @@ void e1000_i2c_bus_clear(struct e1000_hw *hw)
 	/* Put the i2c bus back to default state */
 	e1000_i2c_stop(hw);
 }
-
diff --git a/sys/dev/e1000/e1000_i210.c b/sys/dev/e1000/e1000_i210.c
index 4005034d7d31..a38e1c10ab67 100644
--- a/sys/dev/e1000/e1000_i210.c
+++ b/sys/dev/e1000/e1000_i210.c
@@ -37,6 +37,7 @@
 
 static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw);
 static void e1000_release_nvm_i210(struct e1000_hw *hw);
+static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw);
 static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
 				u16 *data);
 static s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw);
@@ -57,7 +58,7 @@ static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw)
 
 	DEBUGFUNC("e1000_acquire_nvm_i210");
 
-	ret_val = e1000_acquire_swfw_sync(hw, E1000_SWFW_EEP_SM);
+	ret_val = e1000_acquire_swfw_sync_i210(hw, E1000_SWFW_EEP_SM);
 
 	return ret_val;
 }
@@ -73,7 +74,128 @@ static void e1000_release_nvm_i210(struct e1000_hw *hw)
 {
 	DEBUGFUNC("e1000_release_nvm_i210");
 
-	e1000_release_swfw_sync(hw, E1000_SWFW_EEP_SM);
+	e1000_release_swfw_sync_i210(hw, E1000_SWFW_EEP_SM);
+}
+
+/**
+ *  e1000_acquire_swfw_sync_i210 - Acquire SW/FW semaphore
+ *  @hw: pointer to the HW structure
+ *  @mask: specifies which semaphore to acquire
+ **/
+s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
+{
+	u32 swfw_sync;
+	u32 swmask = mask;
+	u32 fwmask = mask << 16;
+	s32 ret_val = E1000_SUCCESS;
+	s32 i = 0, timeout = 200;
+
+	DEBUGFUNC("e1000_acquire_swfw_sync_i210");
+	ASSERT_NO_LOCKS();
+
+	while (i < timeout) {
+		if (e1000_get_hw_semaphore_i210(hw)) {
+			ret_val = -E1000_ERR_SWFW_SYNC;
+			goto out;
+		}
+
+		swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+		if (!(swfw_sync & (fwmask | swmask)))
+			break;
+
+		e1000_put_hw_semaphore(hw);
+		msec_delay_irq(5);
+		i++;
+	}
+
+	if (i == timeout) {
+		DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n");
+		ret_val = -E1000_ERR_SWFW_SYNC;
+		goto out;
+	}
+
+	swfw_sync |= swmask;
+	E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+	e1000_put_hw_semaphore(hw);
+
+out:
+	return ret_val;
+}
+
+/**
+ *  e1000_release_swfw_sync_i210 - Release SW/FW semaphore
+ *  @hw: pointer to the HW structure
+ *  @mask: specifies which semaphore to release
+ **/
+void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
+{
+	u32 swfw_sync;
+
+	DEBUGFUNC("e1000_release_swfw_sync_i210");
+
+	while (e1000_get_hw_semaphore_i210(hw) != E1000_SUCCESS)
+		; /* Empty */
+
+	swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+	swfw_sync &= (u32)~mask;
+	E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+	e1000_put_hw_semaphore(hw);
+}
+
+/**
+ *  e1000_get_hw_semaphore_i210 - Acquire hardware semaphore
+ *  @hw: pointer to the HW structure
+ **/
+static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw)
+{
+	u32 swsm;
+	s32 timeout = hw->nvm.word_size + 1;
+	s32 i = 0;
+
+	DEBUGFUNC("e1000_get_hw_semaphore_i210");
+
+	while (i < timeout) {
+		swsm = E1000_READ_REG(hw, E1000_SWSM);
+		if (!(swsm & E1000_SWSM_SMBI))
+			break;
+		usec_delay(50);
+		i++;
+	}
+
+	if (i == timeout) {
+		/* Clear an unintentionally retained semaphore once. */
+		if (hw->dev_spec._82575.clear_semaphore_once) {
+			hw->dev_spec._82575.clear_semaphore_once = false;
+			e1000_put_hw_semaphore(hw);
+			for (i = 0; i < timeout; i++) {
+				swsm = E1000_READ_REG(hw, E1000_SWSM);
+				if (!(swsm & E1000_SWSM_SMBI))
+					break;
+				usec_delay(50);
+			}
+		}
+		if (i == timeout) {
+			DEBUGOUT("Driver can't access device - SMBI bit is set.\n");
+			return -E1000_ERR_NVM;
+		}
+	}
+
+	for (i = 0; i < timeout; i++) {
+		swsm = E1000_READ_REG(hw, E1000_SWSM);
+		E1000_WRITE_REG(hw, E1000_SWSM,
+		    swsm | E1000_SWSM_SWESMBI);
+		if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI)
+			break;
+		usec_delay(50);
+	}
+
+	if (i == timeout) {
+		e1000_put_hw_semaphore(hw);
+		DEBUGOUT("Driver can't access the NVM\n");
+		return -E1000_ERR_NVM;
+	}
+
+	return E1000_SUCCESS;
 }
 
 /**
diff --git a/sys/dev/e1000/e1000_i210.h b/sys/dev/e1000/e1000_i210.h
index 6f82462246e0..1d648ce41261 100644
--- a/sys/dev/e1000/e1000_i210.h
+++ b/sys/dev/e1000/e1000_i210.h
@@ -46,6 +46,8 @@ s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset,
 s32 e1000_read_invm_version(struct e1000_hw *hw,
 			    struct e1000_fw_version *invm_ver);
 s32 e1000_init_hw_i210(struct e1000_hw *hw);
+s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
+void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
 
 #define E1000_STM_OPCODE		0xDB00
 #define E1000_EEPROM_FLASH_SIZE_WORD	0x11
diff --git a/sys/dev/e1000/e1000_mac.c b/sys/dev/e1000/e1000_mac.c
index dcebd9cf1eb9..a2e05498c681 100644
--- a/sys/dev/e1000/e1000_mac.c
+++ b/sys/dev/e1000/e1000_mac.c
@@ -2190,34 +2190,21 @@ s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg,
 }
 
 /**
- *  e1000_get_hw_semaphore - Acquire hardware semaphore
+ *  e1000_get_hw_semaphore_generic - Acquire hardware semaphore
  *  @hw: pointer to the HW structure
  *
  *  Acquire the HW semaphore to access the PHY or NVM
  **/
-s32 e1000_get_hw_semaphore(struct e1000_hw *hw)
+s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw)
 {
 	u32 swsm;
-	s32 fw_timeout = hw->nvm.word_size + 1;
-	s32 sw_timeout = hw->nvm.word_size + 1;
+	s32 timeout = hw->nvm.word_size + 1;
 	s32 i = 0;
 	
-	DEBUGFUNC("e1000_get_hw_semaphore");
-
-	/* _82571 */
-	/* If we have timedout 3 times on trying to acquire
-	 * the inter-port SMBI semaphore, there is old code
-	 * operating on the other port, and it is not
-	 * releasing SMBI. Modify the number of times that
-	 * we try for the semaphore to interwork with this
-	 * older code.
-	 */
-	if (hw->dev_spec._82571.smb_counter > 2)
-		sw_timeout = 1;
-
+	DEBUGFUNC("e1000_get_hw_semaphore_generic");
 
 	/* Get the SW semaphore */
-	while (i < sw_timeout) {
+	while (i < timeout) {
 		swsm = E1000_READ_REG(hw, E1000_SWSM);
 		if (!(swsm & E1000_SWSM_SMBI))
 			break;
@@ -2226,28 +2213,13 @@ s32 e1000_get_hw_semaphore(struct e1000_hw *hw)
 		i++;
 	}
 
-	if (i == sw_timeout) {
+	if (i == timeout) {
 		DEBUGOUT("Driver can't access device - SMBI bit is set.\n");
-		hw->dev_spec._82571.smb_counter++;
+		return -E1000_ERR_NVM;
 	}
 
-	/* In rare circumstances, the SW semaphore may already be held
-	 * unintentionally. Clear the semaphore once before giving up.
-	 */
-	if (hw->dev_spec._82575.clear_semaphore_once) {
-	 	hw->dev_spec._82575.clear_semaphore_once = false;
-		e1000_put_hw_semaphore(hw);
-		for (i = 0; i < fw_timeout; i++) {
-			swsm = E1000_READ_REG(hw, E1000_SWSM);
-	 		if (!(swsm & E1000_SWSM_SMBI))
-	 			break;
-
-	 		usec_delay(50);
-	 	}
-	 }
-
 	/* Get the FW semaphore. */
-	for (i = 0; i < fw_timeout; i++) {
+	for (i = 0; i < timeout; i++) {
 		swsm = E1000_READ_REG(hw, E1000_SWSM);
 		E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI);
 
@@ -2258,7 +2230,7 @@ s32 e1000_get_hw_semaphore(struct e1000_hw *hw)
 		usec_delay(50);
 	}
 
-	if (i == fw_timeout) {
+	if (i == timeout) {
 		/* Release semaphores */
 		e1000_put_hw_semaphore(hw);
 		DEBUGOUT("Driver can't access the NVM\n");
@@ -2308,7 +2280,7 @@ e1000_acquire_swfw_sync(struct e1000_hw *hw, u16 mask)
 	DEBUGFUNC("e1000_acquire_swfw_sync");
 	ASSERT_NO_LOCKS();
 	while (i < timeout) {
-		if (e1000_get_hw_semaphore(hw)) {
+		if (e1000_get_hw_semaphore_generic(hw)) {
 			ret_val = -E1000_ERR_SWFW_SYNC;
 			goto out;
 		}
@@ -2356,7 +2328,7 @@ e1000_release_swfw_sync(struct e1000_hw *hw, u16 mask)
 
 	DEBUGFUNC("e1000_release_swfw_sync");
 
-	while (e1000_get_hw_semaphore(hw) != E1000_SUCCESS)
+	while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS)
 		; /* Empty */
 
 	swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
@@ -2365,4 +2337,3 @@ e1000_release_swfw_sync(struct e1000_hw *hw, u16 mask)
 
 	e1000_put_hw_semaphore(hw);
 }
-
diff --git a/sys/dev/e1000/e1000_mac.h b/sys/dev/e1000/e1000_mac.h
index a0d609e311d9..a08e64c4b23e 100644
--- a/sys/dev/e1000/e1000_mac.h
+++ b/sys/dev/e1000/e1000_mac.h
@@ -91,7 +91,7 @@ void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop);
 void e1000_update_adaptive_generic(struct e1000_hw *hw);
 void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value);
 
-s32  e1000_get_hw_semaphore(struct e1000_hw *hw);
+s32  e1000_get_hw_semaphore_generic(struct e1000_hw *hw);
 void e1000_put_hw_semaphore(struct e1000_hw *hw);
 s32 e1000_acquire_swfw_sync(struct e1000_hw *hw, u16 mask);
 void e1000_release_swfw_sync(struct e1000_hw *hw, u16 mask);