@@ -620,8 +620,8 @@ extern s32 e1000e_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset,
extern s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data);
extern s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset,
u16 *data);
-extern s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
- u32 usec_interval, bool *success);
+extern s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, int intervals,
+ u32 usec_interval, bool *link);
extern s32 e1000e_phy_reset_dsp(struct e1000_hw *hw);
extern void e1000_power_up_phy_copper(struct e1000_hw *hw);
extern void e1000_power_down_phy_copper(struct e1000_hw *hw);
@@ -681,7 +681,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
* link. If so, then we want to get the current speed/duplex
* of the PHY.
*/
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = e1000e_phy_has_link_generic(hw, 0, 0, &link);
if (ret_val)
goto out;
@@ -3527,7 +3527,7 @@ static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw)
* Attempting this while link is negotiating fouled up link
* stability
*/
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = e1000e_phy_has_link_generic(hw, 0, 0, &link);
if (!link)
return 0;
@@ -456,7 +456,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw)
* link. If so, then we want to get the current speed/duplex
* of the PHY.
*/
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = e1000e_phy_has_link_generic(hw, 0, 0, &link);
if (ret_val)
return ret_val;
@@ -1768,46 +1768,74 @@ static s32 e1000_wait_autoneg(struct e1000_hw *hw)
/**
* e1000e_phy_has_link_generic - Polls PHY for link
* @hw: pointer to the HW structure
- * @iterations: number of times to poll for link
+ * @intervals: number of times to delay between polls for link
* @usec_interval: delay between polling attempts
- * @success: pointer to whether polling was successful or not
+ * @link: pointer to whether link was present or not
*
- * Polls the PHY status register for link, 'iterations' number of times.
+ * Polls the PHY status register for link, 'intervals + 1' number of times.
+ * Max run time is approx 'intervals * usec_interval' microseconds.
**/
-s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
- u32 usec_interval, bool *success)
+s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, int intervals,
+ u32 usec_interval, bool *link)
{
- s32 ret_val = 0;
- u16 i, phy_status;
+ int good_reads_phy_status = 0;
+ bool loopback_checked = false;
+ s32 err;
+ u16 reg;
+
+ /* Remember that e1e_rphy may fail because of another entity
+ * (like the firmware) holding the lock, we need to handle
+ * this gracefully - by waiting and trying again.
+ *
+ * Some PHYs require the PHY_STATUS register to be
+ * read twice due to the link bit being sticky.
+ * No harm doing it across the board.
+ *
+ * This first initial read slightly improves the probability of
+ * a successful double read of the PHY_STATUS on the first iteration.
+ * (and thus also whenever this function is called with iterations == 0)
+ */
+ err = e1e_rphy(hw, PHY_STATUS, ®);
+ if (!err) ++good_reads_phy_status;
+
+ for (;;) {
+ err = e1e_rphy(hw, PHY_STATUS, ®);
+ if (!err) {
+ if (++good_reads_phy_status < 2) continue;
+ if (reg & MII_SR_LINK_STATUS) {
+ *link = true;
+ return 0; /* success: link up */
+ }
+ }
+
+ if (!loopback_checked) {
+ /* If the interface is in loopback-mode... */
+ err = e1e_rphy(hw, PHY_CONTROL, ®);
+ if (!err) {
+ loopback_checked = true;
+ if (reg & MII_CR_LOOPBACK) {
+ /* ... fake link up. */
+ *link = true;
+ return 0; /* success: link up */
+ }
+ }
+ }
+
+ if (--intervals < 0) {
+ /* timeout waiting for link to go up (or only errors) */
+ if (good_reads_phy_status < 2) {
+ if (!err) err = -E1000_ERR_PHY;
+ return err; /* failure */
+ }
+ *link = false;
+ return 0; /* success: link down */
+ }
- for (i = 0; i < iterations; i++) {
- /*
- * Some PHYs require the PHY_STATUS register to be read
- * twice due to the link bit being sticky. No harm doing
- * it across the board.
- */
- ret_val = e1e_rphy(hw, PHY_STATUS, &phy_status);
- if (ret_val)
- /*
- * If the first read fails, another entity may have
- * ownership of the resources, wait and try again to
- * see if they have relinquished the resources yet.
- */
- udelay(usec_interval);
- ret_val = e1e_rphy(hw, PHY_STATUS, &phy_status);
- if (ret_val)
- break;
- if (phy_status & MII_SR_LINK_STATUS)
- break;
if (usec_interval >= 1000)
- mdelay(usec_interval/1000);
+ mdelay(usec_interval / 1000);
else
udelay(usec_interval);
}
-
- *success = (i < iterations);
-
- return ret_val;
}
/**
@@ -1943,7 +1971,7 @@ s32 e1000e_get_phy_info_m88(struct e1000_hw *hw)
return -E1000_ERR_CONFIG;
}
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = e1000e_phy_has_link_generic(hw, 0, 0, &link);
if (ret_val)
return ret_val;
@@ -2011,7 +2039,7 @@ s32 e1000e_get_phy_info_igp(struct e1000_hw *hw)
u16 data;
bool link;
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = e1000e_phy_has_link_generic(hw, 0, 0, &link);
if (ret_val)
return ret_val;
@@ -2071,7 +2099,7 @@ s32 e1000_get_phy_info_ife(struct e1000_hw *hw)
u16 data;
bool link;
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = e1000e_phy_has_link_generic(hw, 0, 0, &link);
if (ret_val)
goto out;
@@ -3298,7 +3326,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw)
u16 data;
bool link;
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ ret_val = e1000e_phy_has_link_generic(hw, 0, 0, &link);
if (ret_val)
goto out;
From: Maciej Żenczykowski <maze@google.com> When loopback mode is forced on interface, and if the carrier check returns negative, then force carrier check positive. This is useful when interface does not have carrier and test puts the interface in loopback mode. While we're at it rework the code to fix other bugs in it. --- drivers/net/ethernet/intel/e1000e/e1000.h | 4 +- drivers/net/ethernet/intel/e1000e/ich8lan.c | 4 +- drivers/net/ethernet/intel/e1000e/lib.c | 2 +- drivers/net/ethernet/intel/e1000e/phy.c | 98 +++++++++++++++++---------- 4 files changed, 68 insertions(+), 40 deletions(-)