e1000e: Serdes - attempt autoneg when link restored.

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>
This commit is contained in:
dave graham 2009-02-10 12:52:28 +00:00 committed by David S. Miller
parent 573cca8c6f
commit c9523379d6
2 changed files with 139 additions and 1 deletions

View 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;
}
@ -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

View 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 {