[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