igb: Fix SerDes autoneg flow control.
This patch enables flow control to be set in SerDes autoneg mode. This is done the way it is done for copper, but relies on a different set of register/bit checks since this is all done within the MAC registers. Signed-off-by: Carolyn Wyborny <carolyn.wyborny@intel.com> Tested-by: Aaron Brown <aaron.f.brown@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
parent
3860a0bf74
commit
daf56e406a
|
@ -1028,6 +1028,15 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
|
||||||
* continue to check for link.
|
* continue to check for link.
|
||||||
*/
|
*/
|
||||||
hw->mac.get_link_status = !hw->mac.serdes_has_link;
|
hw->mac.get_link_status = !hw->mac.serdes_has_link;
|
||||||
|
|
||||||
|
/* Configure Flow Control now that Auto-Neg has completed.
|
||||||
|
* First, we need to restore the desired flow control
|
||||||
|
* settings because we may have had to re-autoneg with a
|
||||||
|
* different link partner.
|
||||||
|
*/
|
||||||
|
ret_val = igb_config_fc_after_link_up(hw);
|
||||||
|
if (ret_val)
|
||||||
|
hw_dbg("Error configuring flow control\n");
|
||||||
} else {
|
} else {
|
||||||
ret_val = igb_check_for_copper_link(hw);
|
ret_val = igb_check_for_copper_link(hw);
|
||||||
}
|
}
|
||||||
|
@ -1345,7 +1354,7 @@ out:
|
||||||
**/
|
**/
|
||||||
static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
|
static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
|
||||||
{
|
{
|
||||||
u32 ctrl_ext, ctrl_reg, reg;
|
u32 ctrl_ext, ctrl_reg, reg, anadv_reg;
|
||||||
bool pcs_autoneg;
|
bool pcs_autoneg;
|
||||||
s32 ret_val = E1000_SUCCESS;
|
s32 ret_val = E1000_SUCCESS;
|
||||||
u16 data;
|
u16 data;
|
||||||
|
@ -1433,27 +1442,45 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
|
||||||
reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP |
|
reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP |
|
||||||
E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK);
|
E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK);
|
||||||
|
|
||||||
/*
|
|
||||||
* We force flow control to prevent the CTRL register values from being
|
|
||||||
* overwritten by the autonegotiated flow control values
|
|
||||||
*/
|
|
||||||
reg |= E1000_PCS_LCTL_FORCE_FCTRL;
|
|
||||||
|
|
||||||
if (pcs_autoneg) {
|
if (pcs_autoneg) {
|
||||||
/* Set PCS register for autoneg */
|
/* Set PCS register for autoneg */
|
||||||
reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */
|
reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */
|
||||||
E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */
|
E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */
|
||||||
|
|
||||||
|
/* Disable force flow control for autoneg */
|
||||||
|
reg &= ~E1000_PCS_LCTL_FORCE_FCTRL;
|
||||||
|
|
||||||
|
/* Configure flow control advertisement for autoneg */
|
||||||
|
anadv_reg = rd32(E1000_PCS_ANADV);
|
||||||
|
anadv_reg &= ~(E1000_TXCW_ASM_DIR | E1000_TXCW_PAUSE);
|
||||||
|
switch (hw->fc.requested_mode) {
|
||||||
|
case e1000_fc_full:
|
||||||
|
case e1000_fc_rx_pause:
|
||||||
|
anadv_reg |= E1000_TXCW_ASM_DIR;
|
||||||
|
anadv_reg |= E1000_TXCW_PAUSE;
|
||||||
|
break;
|
||||||
|
case e1000_fc_tx_pause:
|
||||||
|
anadv_reg |= E1000_TXCW_ASM_DIR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wr32(E1000_PCS_ANADV, anadv_reg);
|
||||||
|
|
||||||
hw_dbg("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg);
|
hw_dbg("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg);
|
||||||
} else {
|
} else {
|
||||||
/* Set PCS register for forced link */
|
/* Set PCS register for forced link */
|
||||||
reg |= E1000_PCS_LCTL_FSD; /* Force Speed */
|
reg |= E1000_PCS_LCTL_FSD; /* Force Speed */
|
||||||
|
|
||||||
|
/* Force flow control for forced link */
|
||||||
|
reg |= E1000_PCS_LCTL_FORCE_FCTRL;
|
||||||
|
|
||||||
hw_dbg("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg);
|
hw_dbg("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
wr32(E1000_PCS_LCTL, reg);
|
wr32(E1000_PCS_LCTL, reg);
|
||||||
|
|
||||||
if (!igb_sgmii_active_82575(hw))
|
if (!pcs_autoneg && !igb_sgmii_active_82575(hw))
|
||||||
igb_force_mac_fc(hw);
|
igb_force_mac_fc(hw);
|
||||||
|
|
||||||
return ret_val;
|
return ret_val;
|
||||||
|
|
|
@ -431,6 +431,10 @@
|
||||||
#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100
|
#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100
|
||||||
#define FLOW_CONTROL_TYPE 0x8808
|
#define FLOW_CONTROL_TYPE 0x8808
|
||||||
|
|
||||||
|
/* Transmit Config Word */
|
||||||
|
#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */
|
||||||
|
#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */
|
||||||
|
|
||||||
/* 802.1q VLAN Packet Size */
|
/* 802.1q VLAN Packet Size */
|
||||||
#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */
|
#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */
|
||||||
#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */
|
#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */
|
||||||
|
@ -539,6 +543,9 @@
|
||||||
/* mPHY Near End Digital Loopback Override Bit */
|
/* mPHY Near End Digital Loopback Override Bit */
|
||||||
#define E1000_MPHY_PCS_CLK_REG_DIGINELBEN 0x10
|
#define E1000_MPHY_PCS_CLK_REG_DIGINELBEN 0x10
|
||||||
|
|
||||||
|
#define E1000_PCS_LCTL_FORCE_FCTRL 0x80
|
||||||
|
#define E1000_PCS_LSTS_AN_COMPLETE 0x10000
|
||||||
|
|
||||||
/* PHY Control Register */
|
/* PHY Control Register */
|
||||||
#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
|
#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
|
||||||
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
|
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
|
||||||
|
|
|
@ -839,6 +839,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
|
||||||
{
|
{
|
||||||
struct e1000_mac_info *mac = &hw->mac;
|
struct e1000_mac_info *mac = &hw->mac;
|
||||||
s32 ret_val = 0;
|
s32 ret_val = 0;
|
||||||
|
u32 pcs_status_reg, pcs_adv_reg, pcs_lp_ability_reg, pcs_ctrl_reg;
|
||||||
u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
|
u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
|
||||||
u16 speed, duplex;
|
u16 speed, duplex;
|
||||||
|
|
||||||
|
@ -1040,6 +1041,129 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Check for the case where we have SerDes media and auto-neg is
|
||||||
|
* enabled. In this case, we need to check and see if Auto-Neg
|
||||||
|
* has completed, and if so, how the PHY and link partner has
|
||||||
|
* flow control configured.
|
||||||
|
*/
|
||||||
|
if ((hw->phy.media_type == e1000_media_type_internal_serdes)
|
||||||
|
&& mac->autoneg) {
|
||||||
|
/* Read the PCS_LSTS and check to see if AutoNeg
|
||||||
|
* has completed.
|
||||||
|
*/
|
||||||
|
pcs_status_reg = rd32(E1000_PCS_LSTAT);
|
||||||
|
|
||||||
|
if (!(pcs_status_reg & E1000_PCS_LSTS_AN_COMPLETE)) {
|
||||||
|
hw_dbg("PCS Auto Neg has not completed.\n");
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The AutoNeg process has completed, so we now need to
|
||||||
|
* read both the Auto Negotiation Advertisement
|
||||||
|
* Register (PCS_ANADV) and the Auto_Negotiation Base
|
||||||
|
* Page Ability Register (PCS_LPAB) to determine how
|
||||||
|
* flow control was negotiated.
|
||||||
|
*/
|
||||||
|
pcs_adv_reg = rd32(E1000_PCS_ANADV);
|
||||||
|
pcs_lp_ability_reg = rd32(E1000_PCS_LPAB);
|
||||||
|
|
||||||
|
/* Two bits in the Auto Negotiation Advertisement Register
|
||||||
|
* (PCS_ANADV) and two bits in the Auto Negotiation Base
|
||||||
|
* Page Ability Register (PCS_LPAB) determine flow control
|
||||||
|
* for both the PHY and the link partner. The following
|
||||||
|
* table, taken out of the IEEE 802.3ab/D6.0 dated March 25,
|
||||||
|
* 1999, describes these PAUSE resolution bits and how flow
|
||||||
|
* control is determined based upon these settings.
|
||||||
|
* NOTE: DC = Don't Care
|
||||||
|
*
|
||||||
|
* LOCAL DEVICE | LINK PARTNER
|
||||||
|
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution
|
||||||
|
*-------|---------|-------|---------|--------------------
|
||||||
|
* 0 | 0 | DC | DC | e1000_fc_none
|
||||||
|
* 0 | 1 | 0 | DC | e1000_fc_none
|
||||||
|
* 0 | 1 | 1 | 0 | e1000_fc_none
|
||||||
|
* 0 | 1 | 1 | 1 | e1000_fc_tx_pause
|
||||||
|
* 1 | 0 | 0 | DC | e1000_fc_none
|
||||||
|
* 1 | DC | 1 | DC | e1000_fc_full
|
||||||
|
* 1 | 1 | 0 | 0 | e1000_fc_none
|
||||||
|
* 1 | 1 | 0 | 1 | e1000_fc_rx_pause
|
||||||
|
*
|
||||||
|
* Are both PAUSE bits set to 1? If so, this implies
|
||||||
|
* Symmetric Flow Control is enabled at both ends. The
|
||||||
|
* ASM_DIR bits are irrelevant per the spec.
|
||||||
|
*
|
||||||
|
* For Symmetric Flow Control:
|
||||||
|
*
|
||||||
|
* LOCAL DEVICE | LINK PARTNER
|
||||||
|
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
|
||||||
|
*-------|---------|-------|---------|--------------------
|
||||||
|
* 1 | DC | 1 | DC | e1000_fc_full
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if ((pcs_adv_reg & E1000_TXCW_PAUSE) &&
|
||||||
|
(pcs_lp_ability_reg & E1000_TXCW_PAUSE)) {
|
||||||
|
/* Now we need to check if the user selected Rx ONLY
|
||||||
|
* of pause frames. In this case, we had to advertise
|
||||||
|
* FULL flow control because we could not advertise Rx
|
||||||
|
* ONLY. Hence, we must now check to see if we need to
|
||||||
|
* turn OFF the TRANSMISSION of PAUSE frames.
|
||||||
|
*/
|
||||||
|
if (hw->fc.requested_mode == e1000_fc_full) {
|
||||||
|
hw->fc.current_mode = e1000_fc_full;
|
||||||
|
hw_dbg("Flow Control = FULL.\n");
|
||||||
|
} else {
|
||||||
|
hw->fc.current_mode = e1000_fc_rx_pause;
|
||||||
|
hw_dbg("Flow Control = Rx PAUSE frames only.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* For receiving PAUSE frames ONLY.
|
||||||
|
*
|
||||||
|
* LOCAL DEVICE | LINK PARTNER
|
||||||
|
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
|
||||||
|
*-------|---------|-------|---------|--------------------
|
||||||
|
* 0 | 1 | 1 | 1 | e1000_fc_tx_pause
|
||||||
|
*/
|
||||||
|
else if (!(pcs_adv_reg & E1000_TXCW_PAUSE) &&
|
||||||
|
(pcs_adv_reg & E1000_TXCW_ASM_DIR) &&
|
||||||
|
(pcs_lp_ability_reg & E1000_TXCW_PAUSE) &&
|
||||||
|
(pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) {
|
||||||
|
hw->fc.current_mode = e1000_fc_tx_pause;
|
||||||
|
hw_dbg("Flow Control = Tx PAUSE frames only.\n");
|
||||||
|
}
|
||||||
|
/* For transmitting PAUSE frames ONLY.
|
||||||
|
*
|
||||||
|
* LOCAL DEVICE | LINK PARTNER
|
||||||
|
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
|
||||||
|
*-------|---------|-------|---------|--------------------
|
||||||
|
* 1 | 1 | 0 | 1 | e1000_fc_rx_pause
|
||||||
|
*/
|
||||||
|
else if ((pcs_adv_reg & E1000_TXCW_PAUSE) &&
|
||||||
|
(pcs_adv_reg & E1000_TXCW_ASM_DIR) &&
|
||||||
|
!(pcs_lp_ability_reg & E1000_TXCW_PAUSE) &&
|
||||||
|
(pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) {
|
||||||
|
hw->fc.current_mode = e1000_fc_rx_pause;
|
||||||
|
hw_dbg("Flow Control = Rx PAUSE frames only.\n");
|
||||||
|
} else {
|
||||||
|
/* Per the IEEE spec, at this point flow control
|
||||||
|
* should be disabled.
|
||||||
|
*/
|
||||||
|
hw->fc.current_mode = e1000_fc_none;
|
||||||
|
hw_dbg("Flow Control = NONE.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we call a subroutine to actually force the MAC
|
||||||
|
* controller to use the correct flow control settings.
|
||||||
|
*/
|
||||||
|
pcs_ctrl_reg = rd32(E1000_PCS_LCTL);
|
||||||
|
pcs_ctrl_reg |= E1000_PCS_LCTL_FORCE_FCTRL;
|
||||||
|
wr32(E1000_PCS_LCTL, pcs_ctrl_reg);
|
||||||
|
|
||||||
|
ret_val = igb_force_mac_fc(hw);
|
||||||
|
if (ret_val) {
|
||||||
|
hw_dbg("Error forcing flow control settings\n");
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return ret_val;
|
return ret_val;
|
||||||
|
|
Reference in New Issue