diff options
author | Bruce Allan <bruce.w.allan@intel.com> | 2009-12-01 15:49:51 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-02 00:35:53 -0800 |
commit | 1a40d5c170f472d541844cb5b4292efbe02ef89c (patch) | |
tree | 33a85e29de0f6563f66387ac32bfeb1e8f4dcdd8 /drivers/net | |
parent | 17f208deb9bf88315aa72c08c866a235c399fb9a (diff) |
e1000e: ensure the link state is correct for serdes links
This patch ensures that the link state (as reported in
mac->serdes_has_link) will transition to false when autoneg fails to
complete but valid codewords were detected.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/e1000e/82571.c | 76 |
1 files changed, 48 insertions, 28 deletions
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c index 26ea5d57a35..227f3d03ba2 100644 --- a/drivers/net/e1000e/82571.c +++ b/drivers/net/e1000e/82571.c @@ -1356,8 +1356,20 @@ static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw) * e1000_check_for_serdes_link_82571 - Check for link (Serdes) * @hw: pointer to the HW structure * - * Checks for link up on the hardware. If link is not up and we have - * a signal, then we need to force link up. + * Reports the link state as up or down. + * + * If autonegotiation is supported by the link partner, the link state is + * determined by the result of autonegotiation. This is the most likely case. + * If autonegotiation is not supported by the link partner, and the link + * has a valid signal, force the link up. + * + * The link state is represented internally here by 4 states: + * + * 1) down + * 2) autoneg_progress + * 3) autoneg_complete (the link sucessfully autonegotiated) + * 4) forced_up (the link has been forced up, it did not autonegotiate) + * **/ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) { @@ -1383,6 +1395,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) */ mac->serdes_link_state = e1000_serdes_link_autoneg_progress; + mac->serdes_has_link = false; e_dbg("AN_UP -> AN_PROG\n"); } break; @@ -1397,57 +1410,64 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) if (rxcw & E1000_RXCW_C) { /* Enable autoneg, and unforce link up */ ew32(TXCW, mac->txcw); - ew32(CTRL, - (ctrl & ~E1000_CTRL_SLU)); + ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); mac->serdes_link_state = e1000_serdes_link_autoneg_progress; + mac->serdes_has_link = false; e_dbg("FORCED_UP -> AN_PROG\n"); } break; case e1000_serdes_link_autoneg_progress: - /* - * If the LU bit is set in the STATUS register, - * autoneg has completed sucessfully. If not, - * try foring the link because the far end may be - * available but not capable of autonegotiation. - */ - if (status & E1000_STATUS_LU) { - mac->serdes_link_state = - e1000_serdes_link_autoneg_complete; - e_dbg("AN_PROG -> AN_UP\n"); + if (rxcw & E1000_RXCW_C) { + /* + * We received /C/ ordered sets, meaning the + * link partner has autonegotiated, and we can + * trust the Link Up (LU) status bit. + */ + if (status & E1000_STATUS_LU) { + mac->serdes_link_state = + e1000_serdes_link_autoneg_complete; + e_dbg("AN_PROG -> AN_UP\n"); + mac->serdes_has_link = true; + } else { + /* Autoneg completed, but failed. */ + mac->serdes_link_state = + e1000_serdes_link_down; + e_dbg("AN_PROG -> DOWN\n"); + } } else { /* - * Disable autoneg, force link up and - * full duplex, and change state to forced + * The link partner did not autoneg. + * Force link up and full duplex, and change + * state to forced. */ - ew32(TXCW, - (mac->txcw & ~E1000_TXCW_ANE)); + ew32(TXCW, (mac->txcw & ~E1000_TXCW_ANE)); ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); ew32(CTRL, ctrl); /* Configure Flow Control after link up. */ - ret_val = - e1000e_config_fc_after_link_up(hw); + ret_val = e1000e_config_fc_after_link_up(hw); if (ret_val) { e_dbg("Error config flow control\n"); break; } mac->serdes_link_state = e1000_serdes_link_forced_up; + mac->serdes_has_link = true; e_dbg("AN_PROG -> FORCED_UP\n"); } - mac->serdes_has_link = true; break; case e1000_serdes_link_down: default: - /* The link was down but the receiver has now gained + /* + * The link was down but the receiver has now gained * valid sync, so lets see if we can bring the link - * up. */ + * up. + */ ew32(TXCW, mac->txcw); - ew32(CTRL, - (ctrl & ~E1000_CTRL_SLU)); + ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); mac->serdes_link_state = e1000_serdes_link_autoneg_progress; e_dbg("DOWN -> AN_PROG\n"); @@ -1460,9 +1480,9 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) e_dbg("ANYSTATE -> DOWN\n"); } else { /* - * We have sync, and can tolerate one - * invalid (IV) codeword before declaring - * link down, so reread to look again + * We have sync, and can tolerate one invalid (IV) + * codeword before declaring link down, so reread + * to look again. */ udelay(10); rxcw = er32(RXCW); |