Skip to content

Commit

Permalink
e1000e: Serdes - attempt autoneg when link restored.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dave graham authored and David S. Miller committed Feb 11, 2009
1 parent 573cca8 commit c952337
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 1 deletion.
132 changes: 131 additions & 1 deletion drivers/net/e1000e/82571.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -1214,6 +1219,131 @@ static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw)
return e1000e_setup_fiber_serdes_link(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
Expand Down
8 changes: 8 additions & 0 deletions drivers/net/e1000e/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit c952337

Please sign in to comment.