From: Mateusz Fryze <[email protected]>

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

Signed-off-by: Mateusz Fryze <[email protected]>
Signed-off-by: Ciara Loftus <[email protected]>
---
 .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 <[email protected]>
 Masoumeh Farhadi Nia <[email protected]>
 Matan Azrad <[email protected]> <[email protected]>
 Matej Vido <[email protected]> <[email protected]>
+Mateusz Fryze <[email protected]>
 Mateusz Kowalski <[email protected]>
 Mateusz Pacuszka <[email protected]>
 Mateusz Polchlopek <[email protected]>
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

Reply via email to