e1000e: ensure the link state is correct for serdes links
authorBruce Allan <bruce.w.allan@intel.com>
Tue, 1 Dec 2009 15:49:51 +0000 (15:49 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Dec 2009 08:35:53 +0000 (00:35 -0800)
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>
drivers/net/e1000e/82571.c

index 26ea5d5..227f3d0 100644 (file)
@@ -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);