summaryrefslogtreecommitdiff
path: root/drivers/net/e1000e/82571.c
diff options
context:
space:
mode:
authorBruce Allan <bruce.w.allan@intel.com>2009-12-01 15:49:51 +0000
committerDavid S. Miller <davem@davemloft.net>2009-12-02 00:35:53 -0800
commit1a40d5c170f472d541844cb5b4292efbe02ef89c (patch)
tree33a85e29de0f6563f66387ac32bfeb1e8f4dcdd8 /drivers/net/e1000e/82571.c
parent17f208deb9bf88315aa72c08c866a235c399fb9a (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/e1000e/82571.c')
-rw-r--r--drivers/net/e1000e/82571.c76
1 files changed, 48 insertions, 28 deletions
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c
index 26ea5d57a354..227f3d03ba2a 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);