]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
e1000e: Workaround for sporadic MDI error on Meteor Lake systems
authorVitaly Lifshits <vitaly.lifshits@intel.com>
Thu, 4 Jan 2024 14:16:52 +0000 (16:16 +0200)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Wed, 27 Mar 2024 18:44:20 +0000 (11:44 -0700)
On some Meteor Lake systems accessing the PHY via the MDIO interface may
result in an MDI error. This issue happens sporadically and in most cases
a second access to the PHY via the MDIO interface results in success.

As a workaround, introduce a retry counter which is set to 3 on Meteor
Lake systems. The driver will only return an error if 3 consecutive PHY
access attempts fail. The retry mechanism is disabled in specific flows,
where MDI errors are expected.

Fixes: cc23f4f0b6b9 ("e1000e: Add support for Meteor Lake")
Suggested-by: Nikolay Mushayev <nikolay.mushayev@intel.com>
Co-developed-by: Nir Efrati <nir.efrati@intel.com>
Signed-off-by: Nir Efrati <nir.efrati@intel.com>
Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com>
Tested-by: Naama Meir <naamax.meir@linux.intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/e1000e/hw.h
drivers/net/ethernet/intel/e1000e/ich8lan.c
drivers/net/ethernet/intel/e1000e/phy.c
drivers/net/ethernet/intel/e1000e/phy.h

index 1fef6bb5a5fbc8603c2ca079e7b18c4221032189..4b6e7536170abc584a7dc95c6c80b926d7c17375 100644 (file)
@@ -628,6 +628,7 @@ struct e1000_phy_info {
        u32 id;
        u32 reset_delay_us;     /* in usec */
        u32 revision;
+       u32 retry_count;
 
        enum e1000_media_type media_type;
 
@@ -644,6 +645,7 @@ struct e1000_phy_info {
        bool polarity_correction;
        bool speed_downgraded;
        bool autoneg_wait_to_complete;
+       bool retry_enabled;
 };
 
 struct e1000_nvm_info {
index 19e450a5bd314ff67676843a767ec1c5c2bd84d4..d8e97669f31b0bcec258740b80525e19541928b8 100644 (file)
@@ -222,11 +222,18 @@ out:
        if (hw->mac.type >= e1000_pch_lpt) {
                /* Only unforce SMBus if ME is not active */
                if (!(er32(FWSM) & E1000_ICH_FWSM_FW_VALID)) {
+                       /* Switching PHY interface always returns MDI error
+                        * so disable retry mechanism to avoid wasting time
+                        */
+                       e1000e_disable_phy_retry(hw);
+
                        /* Unforce SMBus mode in PHY */
                        e1e_rphy_locked(hw, CV_SMB_CTRL, &phy_reg);
                        phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
                        e1e_wphy_locked(hw, CV_SMB_CTRL, phy_reg);
 
+                       e1000e_enable_phy_retry(hw);
+
                        /* Unforce SMBus mode in MAC */
                        mac_reg = er32(CTRL_EXT);
                        mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
@@ -310,6 +317,11 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
                goto out;
        }
 
+       /* There is no guarantee that the PHY is accessible at this time
+        * so disable retry mechanism to avoid wasting time
+        */
+       e1000e_disable_phy_retry(hw);
+
        /* The MAC-PHY interconnect may be in SMBus mode.  If the PHY is
         * inaccessible and resetting the PHY is not blocked, toggle the
         * LANPHYPC Value bit to force the interconnect to PCIe mode.
@@ -380,6 +392,8 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
                break;
        }
 
+       e1000e_enable_phy_retry(hw);
+
        hw->phy.ops.release(hw);
        if (!ret_val) {
 
@@ -449,6 +463,11 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
 
        phy->id = e1000_phy_unknown;
 
+       if (hw->mac.type == e1000_pch_mtp) {
+               phy->retry_count = 2;
+               e1000e_enable_phy_retry(hw);
+       }
+
        ret_val = e1000_init_phy_workarounds_pchlan(hw);
        if (ret_val)
                return ret_val;
@@ -1146,6 +1165,11 @@ s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx)
        if (ret_val)
                goto out;
 
+       /* Switching PHY interface always returns MDI error
+        * so disable retry mechanism to avoid wasting time
+        */
+       e1000e_disable_phy_retry(hw);
+
        /* Force SMBus mode in PHY */
        ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
        if (ret_val)
@@ -1153,6 +1177,8 @@ s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx)
        phy_reg |= CV_SMB_CTRL_FORCE_SMBUS;
        e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);
 
+       e1000e_enable_phy_retry(hw);
+
        /* Force SMBus mode in MAC */
        mac_reg = er32(CTRL_EXT);
        mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
@@ -1313,6 +1339,11 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
                /* Toggle LANPHYPC Value bit */
                e1000_toggle_lanphypc_pch_lpt(hw);
 
+       /* Switching PHY interface always returns MDI error
+        * so disable retry mechanism to avoid wasting time
+        */
+       e1000e_disable_phy_retry(hw);
+
        /* Unforce SMBus mode in PHY */
        ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
        if (ret_val) {
@@ -1333,6 +1364,8 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
        phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
        e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);
 
+       e1000e_enable_phy_retry(hw);
+
        /* Unforce SMBus mode in MAC */
        mac_reg = er32(CTRL_EXT);
        mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
index 5e329156d1bae0880b3a44a8952b484c080db18e..93544f1cc2a51be0c84c33391211bf47d2675edb 100644 (file)
@@ -107,6 +107,16 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw)
        return e1e_wphy(hw, M88E1000_PHY_GEN_CONTROL, 0);
 }
 
+void e1000e_disable_phy_retry(struct e1000_hw *hw)
+{
+       hw->phy.retry_enabled = false;
+}
+
+void e1000e_enable_phy_retry(struct e1000_hw *hw)
+{
+       hw->phy.retry_enabled = true;
+}
+
 /**
  *  e1000e_read_phy_reg_mdic - Read MDI control register
  *  @hw: pointer to the HW structure
@@ -118,55 +128,73 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw)
  **/
 s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
 {
+       u32 i, mdic = 0, retry_counter, retry_max;
        struct e1000_phy_info *phy = &hw->phy;
-       u32 i, mdic = 0;
+       bool success;
 
        if (offset > MAX_PHY_REG_ADDRESS) {
                e_dbg("PHY Address %d is out of range\n", offset);
                return -E1000_ERR_PARAM;
        }
 
+       retry_max = phy->retry_enabled ? phy->retry_count : 0;
+
        /* Set up Op-code, Phy Address, and register offset in the MDI
         * Control register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
-       mdic = ((offset << E1000_MDIC_REG_SHIFT) |
-               (phy->addr << E1000_MDIC_PHY_SHIFT) |
-               (E1000_MDIC_OP_READ));
+       for (retry_counter = 0; retry_counter <= retry_max; retry_counter++) {
+               success = true;
 
-       ew32(MDIC, mdic);
+               mdic = ((offset << E1000_MDIC_REG_SHIFT) |
+                       (phy->addr << E1000_MDIC_PHY_SHIFT) |
+                       (E1000_MDIC_OP_READ));
 
-       /* Poll the ready bit to see if the MDI read completed
-        * Increasing the time out as testing showed failures with
-        * the lower time out
-        */
-       for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
-               udelay(50);
-               mdic = er32(MDIC);
-               if (mdic & E1000_MDIC_READY)
-                       break;
-       }
-       if (!(mdic & E1000_MDIC_READY)) {
-               e_dbg("MDI Read PHY Reg Address %d did not complete\n", offset);
-               return -E1000_ERR_PHY;
-       }
-       if (mdic & E1000_MDIC_ERROR) {
-               e_dbg("MDI Read PHY Reg Address %d Error\n", offset);
-               return -E1000_ERR_PHY;
-       }
-       if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) {
-               e_dbg("MDI Read offset error - requested %d, returned %d\n",
-                     offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic));
-               return -E1000_ERR_PHY;
+               ew32(MDIC, mdic);
+
+               /* Poll the ready bit to see if the MDI read completed
+                * Increasing the time out as testing showed failures with
+                * the lower time out
+                */
+               for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
+                       usleep_range(50, 60);
+                       mdic = er32(MDIC);
+                       if (mdic & E1000_MDIC_READY)
+                               break;
+               }
+               if (!(mdic & E1000_MDIC_READY)) {
+                       e_dbg("MDI Read PHY Reg Address %d did not complete\n",
+                             offset);
+                       success = false;
+               }
+               if (mdic & E1000_MDIC_ERROR) {
+                       e_dbg("MDI Read PHY Reg Address %d Error\n", offset);
+                       success = false;
+               }
+               if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) {
+                       e_dbg("MDI Read offset error - requested %d, returned %d\n",
+                             offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic));
+                       success = false;
+               }
+
+               /* Allow some time after each MDIC transaction to avoid
+                * reading duplicate data in the next MDIC transaction.
+                */
+               if (hw->mac.type == e1000_pch2lan)
+                       usleep_range(100, 150);
+
+               if (success) {
+                       *data = (u16)mdic;
+                       return 0;
+               }
+
+               if (retry_counter != retry_max) {
+                       e_dbg("Perform retry on PHY transaction...\n");
+                       mdelay(10);
+               }
        }
-       *data = (u16)mdic;
 
-       /* Allow some time after each MDIC transaction to avoid
-        * reading duplicate data in the next MDIC transaction.
-        */
-       if (hw->mac.type == e1000_pch2lan)
-               udelay(100);
-       return 0;
+       return -E1000_ERR_PHY;
 }
 
 /**
@@ -179,56 +207,72 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
  **/
 s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
 {
+       u32 i, mdic = 0, retry_counter, retry_max;
        struct e1000_phy_info *phy = &hw->phy;
-       u32 i, mdic = 0;
+       bool success;
 
        if (offset > MAX_PHY_REG_ADDRESS) {
                e_dbg("PHY Address %d is out of range\n", offset);
                return -E1000_ERR_PARAM;
        }
 
+       retry_max = phy->retry_enabled ? phy->retry_count : 0;
+
        /* Set up Op-code, Phy Address, and register offset in the MDI
         * Control register.  The MAC will take care of interfacing with the
         * PHY to retrieve the desired data.
         */
-       mdic = (((u32)data) |
-               (offset << E1000_MDIC_REG_SHIFT) |
-               (phy->addr << E1000_MDIC_PHY_SHIFT) |
-               (E1000_MDIC_OP_WRITE));
+       for (retry_counter = 0; retry_counter <= retry_max; retry_counter++) {
+               success = true;
 
-       ew32(MDIC, mdic);
+               mdic = (((u32)data) |
+                       (offset << E1000_MDIC_REG_SHIFT) |
+                       (phy->addr << E1000_MDIC_PHY_SHIFT) |
+                       (E1000_MDIC_OP_WRITE));
 
-       /* Poll the ready bit to see if the MDI read completed
-        * Increasing the time out as testing showed failures with
-        * the lower time out
-        */
-       for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
-               udelay(50);
-               mdic = er32(MDIC);
-               if (mdic & E1000_MDIC_READY)
-                       break;
-       }
-       if (!(mdic & E1000_MDIC_READY)) {
-               e_dbg("MDI Write PHY Reg Address %d did not complete\n", offset);
-               return -E1000_ERR_PHY;
-       }
-       if (mdic & E1000_MDIC_ERROR) {
-               e_dbg("MDI Write PHY Red Address %d Error\n", offset);
-               return -E1000_ERR_PHY;
-       }
-       if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) {
-               e_dbg("MDI Write offset error - requested %d, returned %d\n",
-                     offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic));
-               return -E1000_ERR_PHY;
-       }
+               ew32(MDIC, mdic);
 
-       /* Allow some time after each MDIC transaction to avoid
-        * reading duplicate data in the next MDIC transaction.
-        */
-       if (hw->mac.type == e1000_pch2lan)
-               udelay(100);
+               /* Poll the ready bit to see if the MDI read completed
+                * Increasing the time out as testing showed failures with
+                * the lower time out
+                */
+               for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) {
+                       usleep_range(50, 60);
+                       mdic = er32(MDIC);
+                       if (mdic & E1000_MDIC_READY)
+                               break;
+               }
+               if (!(mdic & E1000_MDIC_READY)) {
+                       e_dbg("MDI Write PHY Reg Address %d did not complete\n",
+                             offset);
+                       success = false;
+               }
+               if (mdic & E1000_MDIC_ERROR) {
+                       e_dbg("MDI Write PHY Reg Address %d Error\n", offset);
+                       success = false;
+               }
+               if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) {
+                       e_dbg("MDI Write offset error - requested %d, returned %d\n",
+                             offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic));
+                       success = false;
+               }
 
-       return 0;
+               /* Allow some time after each MDIC transaction to avoid
+                * reading duplicate data in the next MDIC transaction.
+                */
+               if (hw->mac.type == e1000_pch2lan)
+                       usleep_range(100, 150);
+
+               if (success)
+                       return 0;
+
+               if (retry_counter != retry_max) {
+                       e_dbg("Perform retry on PHY transaction...\n");
+                       mdelay(10);
+               }
+       }
+
+       return -E1000_ERR_PHY;
 }
 
 /**
index c48777d09523529c0977f1e19510aeb83d245f44..049bb325b4b14f15c674cff482e9bec68642e9e1 100644 (file)
@@ -51,6 +51,8 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data);
 s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data);
 void e1000_power_up_phy_copper(struct e1000_hw *hw);
 void e1000_power_down_phy_copper(struct e1000_hw *hw);
+void e1000e_disable_phy_retry(struct e1000_hw *hw);
+void e1000e_enable_phy_retry(struct e1000_hw *hw);
 s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
 s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
 s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data);