[PATCH 09/10] net/e1000/base: auto-negotiation status for 1000BASE-T

Ciara Loftus ciara.loftus at intel.com
Wed May 20 14:52:46 CEST 2026


From: Mateusz Fryze <mateusz.fryze at intel.com>

Add functionality to find auto-negotiation status on 1000BASE-T PHYs
based on specific PHY registers.

Signed-off-by: Mateusz Fryze <mateusz.fryze at intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus at intel.com>
---
 .mailmap                                     |  1 +
 drivers/net/intel/e1000/base/e1000_82575.c   |  1 +
 drivers/net/intel/e1000/base/e1000_api.c     | 16 ++++
 drivers/net/intel/e1000/base/e1000_api.h     |  1 +
 drivers/net/intel/e1000/base/e1000_defines.h | 22 ++++++
 drivers/net/intel/e1000/base/e1000_hw.h      |  9 +++
 drivers/net/intel/e1000/base/e1000_phy.c     | 81 ++++++++++++++++++++
 drivers/net/intel/e1000/base/e1000_phy.h     |  2 +
 8 files changed, 133 insertions(+)

diff --git a/.mailmap b/.mailmap
index f3130df686..4a211b1211 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1033,6 +1033,7 @@ Masoud Hasanifard <masoudhasanifard at gmail.com>
 Masoumeh Farhadi Nia <masoumeh.farhadinia at gmail.com>
 Matan Azrad <matan at nvidia.com> <matan at mellanox.com>
 Matej Vido <matejvido at gmail.com> <vido at cesnet.cz>
+Mateusz Fryze <mateusz.fryze at intel.com>
 Mateusz Kowalski <mateusz.kowalski at intel.com>
 Mateusz Pacuszka <mateuszx.pacuszka at intel.com>
 Mateusz Polchlopek <mateusz.polchlopek at intel.com>
diff --git a/drivers/net/intel/e1000/base/e1000_82575.c b/drivers/net/intel/e1000/base/e1000_82575.c
index 8ae2b77d5f..a2b563975e 100644
--- a/drivers/net/intel/e1000/base/e1000_82575.c
+++ b/drivers/net/intel/e1000/base/e1000_82575.c
@@ -233,6 +233,7 @@ STATIC s32 e1000_init_phy_params_82575(struct e1000_hw *hw)
 		phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580;
 		phy->ops.force_speed_duplex =
 				e1000_phy_force_speed_duplex_82577;
+		phy->ops.get_an_status = e1000_1gbase_t_autoneg_status;
 		break;
 	case I210_I_PHY_ID:
 		phy->type		= e1000_phy_i210;
diff --git a/drivers/net/intel/e1000/base/e1000_api.c b/drivers/net/intel/e1000/base/e1000_api.c
index a7877613e3..83b3d6c439 100644
--- a/drivers/net/intel/e1000/base/e1000_api.c
+++ b/drivers/net/intel/e1000/base/e1000_api.c
@@ -1138,6 +1138,22 @@ s32 e1000_get_phy_info(struct e1000_hw *hw)
 	return E1000_SUCCESS;
 }
 
+/**
+ *  e1000_get_an_status - Finds Auto-negotiation status based on PHY registers
+ *  @hw: pointer to the HW structure
+ *  @an_status: AN status
+ *
+ *  This function gets information from the PHY specific registers to determine
+ *  Auto-negotiation status.
+ **/
+s32 e1000_get_an_status(struct e1000_hw *hw, u8 *an_status)
+{
+	if (hw->phy.ops.get_an_status)
+		return hw->phy.ops.get_an_status(hw, an_status);
+
+	return E1000_SUCCESS;
+}
+
 /**
  *  e1000_phy_hw_reset - Hard PHY reset
  *  @hw: pointer to the HW structure
diff --git a/drivers/net/intel/e1000/base/e1000_api.h b/drivers/net/intel/e1000/base/e1000_api.h
index ca3248c214..1f81f49bd5 100644
--- a/drivers/net/intel/e1000/base/e1000_api.h
+++ b/drivers/net/intel/e1000/base/e1000_api.h
@@ -59,6 +59,7 @@ s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data);
 s32 e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, u32 offset,
 			      u8 data);
 s32 e1000_get_phy_info(struct e1000_hw *hw);
+s32 e1000_get_an_status(struct e1000_hw *hw, u8 *an_status);
 void e1000_release_phy(struct e1000_hw *hw);
 s32 e1000_acquire_phy(struct e1000_hw *hw);
 s32 e1000_cfg_on_link_up(struct e1000_hw *hw);
diff --git a/drivers/net/intel/e1000/base/e1000_defines.h b/drivers/net/intel/e1000/base/e1000_defines.h
index 6c710300a6..ba12dde770 100644
--- a/drivers/net/intel/e1000/base/e1000_defines.h
+++ b/drivers/net/intel/e1000/base/e1000_defines.h
@@ -1103,6 +1103,28 @@
 #define PHY_1000T_STATUS	0x0A /* 1000Base-T Status Reg */
 #define PHY_EXT_STATUS		0x0F /* Extended Status Reg */
 
+/* PHY Status Register */
+#define PHY_STATUS_LINK_STATUS_SHIFT		2
+#define PHY_STATUS_AN_ABILITY_SHIFT			3
+#define PHY_STATUS_REMOTE_FAULT_SHIFT		4
+#define PHY_STATUS_AN_COMPLETE_SHIFT		5
+#define PHY_STATUS_LINK_STATUS_MASK			(0x01 << PHY_STATUS_LINK_STATUS_SHIFT)
+#define PHY_STATUS_AN_ABILITY_MASK			(0x01 << PHY_STATUS_AN_ABILITY_SHIFT)
+#define PHY_STATUS_REMOTE_FAULT_MASK		(0x01 << PHY_STATUS_REMOTE_FAULT_SHIFT)
+#define PHY_STATUS_AN_COMPLETE_MASK			(0x01 << PHY_STATUS_AN_COMPLETE_SHIFT)
+
+/* Auto negotiation Expansion Register */
+#define PHY_AUTONEG_EXP_LP_AN_ABLE_SHIFT			0
+#define PHY_AUTONEG_EXP_PAGE_RECVD_SHIFT			1
+#define PHY_AUTONEG_EXP_NEXT_PAGE_ABLE_SHIFT		2
+#define PHY_AUTONEG_EXP_LP_NEXT_PAGE_ABLE_SHIFT		3
+#define PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_SHIFT	4
+#define PHY_AUTONEG_EXP_LP_AN_ABLE_MASK			(0x01 << PHY_AUTONEG_EXP_LP_AN_ABLE_SHIFT)
+#define PHY_AUTONEG_EXP_PAGE_RECVD_MASK			(0x01 << PHY_AUTONEG_EXP_PAGE_RECVD_SHIFT)
+#define PHY_AUTONEG_EXP_NEXT_PAGE_ABLE_MASK	(0x01 << PHY_AUTONEG_EXP_NEXT_PAGE_ABLE_SHIFT)
+#define PHY_AUTONEG_EXP_LP_NEXT_PAGE_ABLE_MASK	(0x01 << PHY_AUTONEG_EXP_LP_NEXT_PAGE_ABLE_SHIFT)
+#define PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_MASK (0x01 << PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_SHIFT)
+
 /* PHY GPY 211 registers */
 #define STANDARD_AN_REG_MASK	0x0007 /* MMD */
 #define ANEG_MULTIGBT_AN_CTRL	0x0020 /* MULTI GBT AN Control Register */
diff --git a/drivers/net/intel/e1000/base/e1000_hw.h b/drivers/net/intel/e1000/base/e1000_hw.h
index 9b1fafd75c..99c2195916 100644
--- a/drivers/net/intel/e1000/base/e1000_hw.h
+++ b/drivers/net/intel/e1000/base/e1000_hw.h
@@ -409,6 +409,14 @@ enum e1000_serdes_link_state {
 	e1000_serdes_link_forced_up
 };
 
+enum e1000_autoneg_status {
+	e1000_an_off = 0,	/* No conn.; AN unsupported, disabled, or disabled on the LP */
+	e1000_an_failed,	/* Remote Fault or Parallel Detection Fault reported */
+	e1000_an_in_progress,
+	e1000_an_complete,
+	e1000_an_status_unavailable	/* AN status could not be obtained */
+};
+
 enum e1000_invm_structure_type {
 	e1000_invm_uninitialized_structure		= 0x00,
 	e1000_invm_word_autoload_structure		= 0x01,
@@ -803,6 +811,7 @@ struct e1000_phy_operations {
 	s32  (*write_reg_page)(struct e1000_hw *, u32, u16);
 	void (*power_up)(struct e1000_hw *);
 	void (*power_down)(struct e1000_hw *);
+	s32  (*get_an_status)(struct e1000_hw *, u8 *);
 	s32 (*read_i2c_byte)(struct e1000_hw *, u8, u8, u8 *);
 	s32 (*write_i2c_byte)(struct e1000_hw *, u8, u8, u8);
 };
diff --git a/drivers/net/intel/e1000/base/e1000_phy.c b/drivers/net/intel/e1000/base/e1000_phy.c
index 31ef5089ba..c66ae44e7a 100644
--- a/drivers/net/intel/e1000/base/e1000_phy.c
+++ b/drivers/net/intel/e1000/base/e1000_phy.c
@@ -63,6 +63,7 @@ void e1000_init_phy_ops_generic(struct e1000_hw *hw)
 	phy->ops.write_reg_page = e1000_null_write_reg;
 	phy->ops.power_up = e1000_null_phy_generic;
 	phy->ops.power_down = e1000_null_phy_generic;
+	phy->ops.get_an_status = e1000_null_an_status;
 	phy->ops.read_i2c_byte = e1000_read_i2c_byte_null;
 	phy->ops.write_i2c_byte = e1000_write_i2c_byte_null;
 	phy->ops.cfg_on_link_up = e1000_null_ops_generic;
@@ -133,6 +134,21 @@ s32 e1000_null_write_reg(struct e1000_hw E1000_UNUSEDARG *hw,
 	return E1000_SUCCESS;
 }
 
+/**
+ *  e1000_null_link_info - No-op function, return 0
+ *  @hw: pointer to the HW structure
+ *  @status: dummy variable
+ **/
+s32 e1000_null_an_status(struct e1000_hw E1000_UNUSEDARG *hw,
+			u8 *status)
+{
+	DEBUGFUNC("e1000_null_an_status");
+	UNREFERENCED_1PARAMETER(hw);
+	*status = e1000_an_status_unavailable;
+
+	return E1000_SUCCESS;
+}
+
 /**
  *  e1000_read_i2c_byte_null - No-op function, return 0
  *  @hw: pointer to hardware structure
@@ -2427,6 +2443,71 @@ STATIC s32 e1000_wait_autoneg(struct e1000_hw *hw)
 	return ret_val;
 }
 
+/**
+ *  e1000_1gbase_t_autoneg_status - Gets information on current AN status.
+ *  @hw: pointer to the HW structure
+ *  @an_status: pointer to the AN status
+ *
+ *  The function finds the Auto-negotiation status of 1000BASE-T PHY based on
+ *  the data from PHY Status (PSTATUS) and Auto–Negotiation Expansion (ANE)
+ *  PHY registers.
+ *
+ *  @note The function will report Auto-negotiation OFF when there is no
+ *  media connected to the port. When used during the PHY reset, it might not
+ *  report a valid status.
+ **/
+s32 e1000_1gbase_t_autoneg_status(struct e1000_hw *hw, u8 *an_status)
+{
+	s32 ret_val = E1000_SUCCESS;
+	u16 phy_reg = 0;
+
+	DEBUGFUNC("e1000_1gbase_t_autoneg_status\n");
+
+	do {
+		*an_status = e1000_an_status_unavailable;
+		ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_reg);
+		if (ret_val) {
+			DEBUGOUT1("Reading PHY STATUS register returned error: %X\n", ret_val);
+			break;
+		}
+
+		if (!(phy_reg & PHY_STATUS_AN_ABILITY_MASK)) {
+			*an_status = e1000_an_off;
+			break;
+		}
+
+		if (phy_reg & (PHY_STATUS_AN_COMPLETE_MASK | PHY_STATUS_LINK_STATUS_MASK)) {
+			*an_status = e1000_an_complete;
+			break;
+		}
+
+		if (phy_reg & PHY_STATUS_REMOTE_FAULT_MASK) {
+			*an_status = e1000_an_failed;
+			break;
+		}
+
+		ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_reg);
+		if (ret_val) {
+			DEBUGOUT1("Reading PHY ANE register returned error: %X\n", ret_val);
+			break;
+		}
+
+		if (!(phy_reg & PHY_AUTONEG_EXP_LP_AN_ABLE_MASK)) {
+			*an_status = e1000_an_off;
+			break;
+		}
+
+		if (phy_reg & PHY_AUTONEG_EXP_PARALLEL_DETECT_FLT_MASK) {
+			*an_status = e1000_an_failed;
+			break;
+		}
+
+		*an_status = e1000_an_in_progress;
+	} while (0);
+
+	return ret_val;
+}
+
 /**
  *  e1000_phy_has_link_generic - Polls PHY for link
  *  @hw: pointer to the HW structure
diff --git a/drivers/net/intel/e1000/base/e1000_phy.h b/drivers/net/intel/e1000/base/e1000_phy.h
index 4c0b93c18f..d8fcc7ae10 100644
--- a/drivers/net/intel/e1000/base/e1000_phy.h
+++ b/drivers/net/intel/e1000/base/e1000_phy.h
@@ -11,6 +11,7 @@ void e1000_null_phy_generic(struct e1000_hw *hw);
 s32  e1000_null_lplu_state(struct e1000_hw *hw, bool active);
 s32  e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data);
 s32  e1000_null_set_page(struct e1000_hw *hw, u16 data);
+s32 e1000_null_an_status(struct e1000_hw *hw, u8 *status);
 s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset,
 			     u8 dev_addr, u8 *data);
 s32 e1000_write_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset,
@@ -22,6 +23,7 @@ s32  e1000_check_polarity_ife(struct e1000_hw *hw);
 s32  e1000_check_reset_block_generic(struct e1000_hw *hw);
 s32  e1000_phy_setup_autoneg(struct e1000_hw *hw);
 s32  e1000_copper_link_autoneg(struct e1000_hw *hw);
+s32  e1000_1gbase_t_autoneg_status(struct e1000_hw *hw, u8 *an_status);
 s32  e1000_copper_link_setup_igp(struct e1000_hw *hw);
 s32  e1000_copper_link_setup_m88(struct e1000_hw *hw);
 s32  e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw);
-- 
2.43.0



More information about the dev mailing list