OSDN Git Service

e1000e: Serdes - attempt autoneg when link restored.
authordave graham <david.graham@intel.com>
Tue, 10 Feb 2009 12:52:28 +0000 (12:52 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 11 Feb 2009 01:00:28 +0000 (17:00 -0800)
This patch addresses an issue where we did not restart auto-negotiation on
serdes links when the link partner was disabled and re-enabled. It includes
reworking the serdes link detect mechanism to be a state machine for
82571 and 82572 parts only.

Signed-off-by: dave graham <david.graham@intel.com>
Acked-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
drivers/net/e1000e/hw.h

index 25f6bc9..565fd4e 100644 (file)
@@ -61,6 +61,7 @@
 static s32 e1000_get_phy_id_82571(struct e1000_hw *hw);
 static s32 e1000_setup_copper_link_82571(struct e1000_hw *hw);
 static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw);
+static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw);
 static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset,
                                      u16 words, u16 *data);
 static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw);
@@ -250,7 +251,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
        case e1000_media_type_internal_serdes:
                func->setup_physical_interface =
                        e1000_setup_fiber_serdes_link_82571;
-               func->check_for_link = e1000e_check_for_serdes_link;
+               func->check_for_link = e1000_check_for_serdes_link_82571;
                func->get_link_up_info =
                        e1000e_get_speed_and_duplex_fiber_serdes;
                break;
@@ -830,6 +831,10 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
                hw->dev_spec.e82571.alt_mac_addr_is_present)
                        e1000e_set_laa_state_82571(hw, true);
 
+       /* Reinitialize the 82571 serdes link state machine */
+       if (hw->phy.media_type == e1000_media_type_internal_serdes)
+               hw->mac.serdes_link_state = e1000_serdes_link_down;
+
        return 0;
 }
 
@@ -1215,6 +1220,131 @@ 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.
+ **/
+s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 rxcw;
+       u32 ctrl;
+       u32 status;
+       s32 ret_val = 0;
+
+       ctrl = er32(CTRL);
+       status = er32(STATUS);
+       rxcw = er32(RXCW);
+
+       if ((rxcw & E1000_RXCW_SYNCH) && !(rxcw & E1000_RXCW_IV)) {
+
+               /* Receiver is synchronized with no invalid bits.  */
+               switch (mac->serdes_link_state) {
+               case e1000_serdes_link_autoneg_complete:
+                       if (!(status & E1000_STATUS_LU)) {
+                               /*
+                                * We have lost link, retry autoneg before
+                                * reporting link failure
+                                */
+                               mac->serdes_link_state =
+                                   e1000_serdes_link_autoneg_progress;
+                               hw_dbg(hw, "AN_UP     -> AN_PROG\n");
+                       }
+               break;
+
+               case e1000_serdes_link_forced_up:
+                       /*
+                        * If we are receiving /C/ ordered sets, re-enable
+                        * auto-negotiation in the TXCW register and disable
+                        * forced link in the Device Control register in an
+                        * attempt to auto-negotiate with our link partner.
+                        */
+                       if (rxcw & E1000_RXCW_C) {
+                               /* Enable autoneg, and unforce link up */
+                               ew32(TXCW, mac->txcw);
+                               ew32(CTRL,
+                                   (ctrl & ~E1000_CTRL_SLU));
+                               mac->serdes_link_state =
+                                   e1000_serdes_link_autoneg_progress;
+                               hw_dbg(hw, "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;
+                               hw_dbg(hw, "AN_PROG   -> AN_UP\n");
+                       } else {
+                               /*
+                                * Disable autoneg, force link up and
+                                * full duplex, and change state to forced
+                                */
+                               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);
+                               if (ret_val) {
+                                       hw_dbg(hw, "Error config flow control\n");
+                                       break;
+                               }
+                               mac->serdes_link_state =
+                                   e1000_serdes_link_forced_up;
+                               hw_dbg(hw, "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
+                        * valid sync, so lets see if we can bring the link
+                        * up. */
+                       ew32(TXCW, mac->txcw);
+                       ew32(CTRL,
+                           (ctrl & ~E1000_CTRL_SLU));
+                       mac->serdes_link_state =
+                           e1000_serdes_link_autoneg_progress;
+                       hw_dbg(hw, "DOWN      -> AN_PROG\n");
+                       break;
+               }
+       } else {
+               if (!(rxcw & E1000_RXCW_SYNCH)) {
+                       mac->serdes_has_link = false;
+                       mac->serdes_link_state = e1000_serdes_link_down;
+                       hw_dbg(hw, "ANYSTATE  -> DOWN\n");
+               } else {
+                       /*
+                        * We have sync, and can tolerate one
+                        * invalid (IV) codeword before declaring
+                        * link down, so reread to look again
+                        */
+                       udelay(10);
+                       rxcw = er32(RXCW);
+                       if (rxcw & E1000_RXCW_IV) {
+                               mac->serdes_link_state = e1000_serdes_link_down;
+                               mac->serdes_has_link = false;
+                               hw_dbg(hw, "ANYSTATE  -> DOWN\n");
+                       }
+               }
+       }
+
+       return ret_val;
+}
+
+/**
  *  e1000_valid_led_default_82571 - Verify a valid default LED config
  *  @hw: pointer to the HW structure
  *  @data: pointer to the NVM (EEPROM)
index 2d4ce04..5cb428c 100644 (file)
@@ -459,6 +459,13 @@ enum e1000_smart_speed {
        e1000_smart_speed_off
 };
 
+enum e1000_serdes_link_state {
+       e1000_serdes_link_down = 0,
+       e1000_serdes_link_autoneg_progress,
+       e1000_serdes_link_autoneg_complete,
+       e1000_serdes_link_forced_up
+};
+
 /* Receive Descriptor */
 struct e1000_rx_desc {
        __le64 buffer_addr; /* Address of the descriptor's data buffer */
@@ -787,6 +794,7 @@ struct e1000_mac_info {
        bool in_ifs_mode;
        bool serdes_has_link;
        bool tx_pkt_filtering;
+       enum e1000_serdes_link_state serdes_link_state;
 };
 
 struct e1000_phy_info {