Skip to content

Commit

Permalink
igb: Fix SerDes autoneg flow control.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Carolyn Wyborny authored and Jeff Kirsher committed Dec 1, 2012
1 parent 3860a0b commit daf56e4
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 8 deletions.
43 changes: 35 additions & 8 deletions drivers/net/ethernet/intel/igb/e1000_82575.c
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,15 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
* continue to check for 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 {
ret_val = igb_check_for_copper_link(hw);
}
Expand Down Expand Up @@ -1345,7 +1354,7 @@ static s32 igb_setup_copper_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;
s32 ret_val = E1000_SUCCESS;
u16 data;
Expand Down Expand Up @@ -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 |
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) {
/* Set PCS register for autoneg */
reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable 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);
} else {
/* Set PCS register for forced link */
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);
}

wr32(E1000_PCS_LCTL, reg);

if (!igb_sgmii_active_82575(hw))
if (!pcs_autoneg && !igb_sgmii_active_82575(hw))
igb_force_mac_fc(hw);

return ret_val;
Expand Down
7 changes: 7 additions & 0 deletions drivers/net/ethernet/intel/igb/e1000_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,10 @@
#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100
#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 */
#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */
#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */
Expand Down Expand Up @@ -539,6 +543,9 @@
/* mPHY Near End Digital Loopback Override Bit */
#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 */
#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
Expand Down
124 changes: 124 additions & 0 deletions drivers/net/ethernet/intel/igb/e1000_mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
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 speed, duplex;

Expand Down Expand Up @@ -1040,6 +1041,129 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
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:
return ret_val;
Expand Down

0 comments on commit daf56e4

Please sign in to comment.