Skip to content

Commit

Permalink
ixgbe: Update flow control state machine in link setup
Browse files Browse the repository at this point in the history
The flow control handling is overly complicated and difficult to maintain.
This patch cleans up the flow control handling and makes it much more
explicit.  It also adds 1G flow control autonegotiation, for 1G copper
links, 1G KX links, and 1G fiber links.

Signed-off-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Peter P Waskiewicz Jr authored and David S. Miller committed Feb 7, 2009
1 parent 3948279 commit 0ecc061
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 122 deletions.
177 changes: 113 additions & 64 deletions drivers/net/ixgbe/ixgbe_82598.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,119 +255,169 @@ static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw)
}

/**
* ixgbe_setup_fc_82598 - Configure flow control settings
* ixgbe_fc_enable_82598 - Enable flow control
* @hw: pointer to hardware structure
* @packetbuf_num: packet buffer number (0-7)
*
* Configures the flow control settings based on SW configuration. This
* function is used for 802.3x flow control configuration only.
* Enable flow control according to the current settings.
**/
static s32 ixgbe_setup_fc_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
{
u32 frctl_reg;
s32 ret_val = 0;
u32 fctrl_reg;
u32 rmcs_reg;
u32 reg;

if (packetbuf_num < 0 || packetbuf_num > 7) {
hw_dbg(hw, "Invalid packet buffer number [%d], expected range is"
" 0-7\n", packetbuf_num);
}

frctl_reg = IXGBE_READ_REG(hw, IXGBE_FCTRL);
frctl_reg &= ~(IXGBE_FCTRL_RFCE | IXGBE_FCTRL_RPFCE);
fctrl_reg = IXGBE_READ_REG(hw, IXGBE_FCTRL);
fctrl_reg &= ~(IXGBE_FCTRL_RFCE | IXGBE_FCTRL_RPFCE);

rmcs_reg = IXGBE_READ_REG(hw, IXGBE_RMCS);
rmcs_reg &= ~(IXGBE_RMCS_TFCE_PRIORITY | IXGBE_RMCS_TFCE_802_3X);

/*
* 10 gig parts do not have a word in the EEPROM to determine the
* default flow control setting, so we explicitly set it to full.
*/
if (hw->fc.type == ixgbe_fc_default)
hw->fc.type = ixgbe_fc_full;

/*
* We want to save off the original Flow Control configuration just in
* case we get disconnected and then reconnected into a different hub
* or switch with different Flow Control capabilities.
*/
hw->fc.original_type = hw->fc.type;

/*
* The possible values of the "flow_control" parameter are:
* The possible values of fc.current_mode are:
* 0: Flow control is completely disabled
* 1: Rx flow control is enabled (we can receive pause frames but not
* send pause frames).
* 2: Tx flow control is enabled (we can send pause frames but we do not
* support receiving pause frames)
* 1: Rx flow control is enabled (we can receive pause frames,
* but not send pause frames).
* 2: Tx flow control is enabled (we can send pause frames but
* we do not support receiving pause frames).
* 3: Both Rx and Tx flow control (symmetric) are enabled.
* other: Invalid.
*/
switch (hw->fc.type) {
switch (hw->fc.current_mode) {
case ixgbe_fc_none:
/* Flow control completely disabled by software override. */
break;
case ixgbe_fc_rx_pause:
/*
* Rx Flow control is enabled,
* and Tx Flow control is disabled.
* Rx Flow control is enabled and Tx Flow control is
* disabled by software override. Since there really
* isn't a way to advertise that we are capable of RX
* Pause ONLY, we will advertise that we support both
* symmetric and asymmetric Rx PAUSE. Later, we will
* disable the adapter's ability to send PAUSE frames.
*/
frctl_reg |= IXGBE_FCTRL_RFCE;
fctrl_reg |= IXGBE_FCTRL_RFCE;
break;
case ixgbe_fc_tx_pause:
/*
* Tx Flow control is enabled, and Rx Flow control is disabled,
* by a software over-ride.
* Tx Flow control is enabled, and Rx Flow control is
* disabled by software override.
*/
rmcs_reg |= IXGBE_RMCS_TFCE_802_3X;
break;
case ixgbe_fc_full:
/*
* Flow control (both Rx and Tx) is enabled by a software
* over-ride.
*/
frctl_reg |= IXGBE_FCTRL_RFCE;
/* Flow control (both Rx and Tx) is enabled by SW override. */
fctrl_reg |= IXGBE_FCTRL_RFCE;
rmcs_reg |= IXGBE_RMCS_TFCE_802_3X;
break;
default:
/* We should never get here. The value should be 0-3. */
hw_dbg(hw, "Flow control param set incorrectly\n");
ret_val = -IXGBE_ERR_CONFIG;
goto out;
break;
}

/* Enable 802.3x based flow control settings. */
IXGBE_WRITE_REG(hw, IXGBE_FCTRL, frctl_reg);
IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl_reg);
IXGBE_WRITE_REG(hw, IXGBE_RMCS, rmcs_reg);

/*
* Check for invalid software configuration, zeros are completely
* invalid for all parameters used past this point, and if we enable
* flow control with zero water marks, we blast flow control packets.
*/
if (!hw->fc.low_water || !hw->fc.high_water || !hw->fc.pause_time) {
hw_dbg(hw, "Flow control structure initialized incorrectly\n");
return IXGBE_ERR_INVALID_LINK_SETTINGS;
}

/*
* We need to set up the Receive Threshold high and low water
* marks as well as (optionally) enabling the transmission of
* XON frames.
*/
if (hw->fc.type & ixgbe_fc_tx_pause) {
/* Set up and enable Rx high/low water mark thresholds, enable XON. */
if (hw->fc.current_mode & ixgbe_fc_tx_pause) {
if (hw->fc.send_xon) {
IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num),
(hw->fc.low_water | IXGBE_FCRTL_XONE));
} else {
IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num),
hw->fc.low_water);
}

IXGBE_WRITE_REG(hw, IXGBE_FCRTH(packetbuf_num),
(hw->fc.high_water)|IXGBE_FCRTH_FCEN);
(hw->fc.high_water | IXGBE_FCRTH_FCEN));
}

IXGBE_WRITE_REG(hw, IXGBE_FCTTV(0), hw->fc.pause_time);
/* Configure pause time (2 TCs per register) */
reg = IXGBE_READ_REG(hw, IXGBE_FCTTV(packetbuf_num));
if ((packetbuf_num & 1) == 0)
reg = (reg & 0xFFFF0000) | hw->fc.pause_time;
else
reg = (reg & 0x0000FFFF) | (hw->fc.pause_time << 16);
IXGBE_WRITE_REG(hw, IXGBE_FCTTV(packetbuf_num / 2), reg);

IXGBE_WRITE_REG(hw, IXGBE_FCRTV, (hw->fc.pause_time >> 1));

return 0;
out:
return ret_val;
}

/**
* ixgbe_setup_fc_82598 - Configure flow control settings
* @hw: pointer to hardware structure
* @packetbuf_num: packet buffer number (0-7)
*
* Configures the flow control settings based on SW configuration. This
* function is used for 802.3x flow control configuration only.
**/
static s32 ixgbe_setup_fc_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
{
s32 ret_val = 0;
ixgbe_link_speed speed;
bool link_up;

/* Validate the packetbuf configuration */
if (packetbuf_num < 0 || packetbuf_num > 7) {
hw_dbg(hw, "Invalid packet buffer number [%d], expected range is"
" 0-7\n", packetbuf_num);
ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
goto out;
}

/*
* Validate the water mark configuration. Zero water marks are invalid
* because it causes the controller to just blast out fc packets.
*/
if (!hw->fc.low_water || !hw->fc.high_water || !hw->fc.pause_time) {
hw_dbg(hw, "Invalid water mark configuration\n");
ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
goto out;
}

/*
* Validate the requested mode. Strict IEEE mode does not allow
* ixgbe_fc_rx_pause because it will cause testing anomalies.
*/
if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
hw_dbg(hw, "ixgbe_fc_rx_pause not valid in strict IEEE mode\n");
ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
goto out;
}

/*
* 10gig parts do not have a word in the EEPROM to determine the
* default flow control setting, so we explicitly set it to full.
*/
if (hw->fc.requested_mode == ixgbe_fc_default)
hw->fc.requested_mode = ixgbe_fc_full;

/*
* Save off the requested flow control mode for use later. Depending
* on the link partner's capabilities, we may or may not use this mode.
*/

hw->fc.current_mode = hw->fc.requested_mode;

/* Decide whether to use autoneg or not. */
hw->mac.ops.check_link(hw, &speed, &link_up, false);
if (hw->phy.multispeed_fiber && (speed == IXGBE_LINK_SPEED_1GB_FULL))
ret_val = ixgbe_fc_autoneg(hw);

if (ret_val)
goto out;

ret_val = ixgbe_fc_enable_82598(hw, packetbuf_num);

out:
return ret_val;
}

/**
Expand Down Expand Up @@ -414,7 +464,6 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw)
* case we get disconnected and then reconnected into a different hub
* or switch with different Flow Control capabilities.
*/
hw->fc.original_type = hw->fc.type;
ixgbe_setup_fc_82598(hw, 0);

/* Add delay to filter out noises during initial link setup */
Expand Down
138 changes: 138 additions & 0 deletions drivers/net/ixgbe/ixgbe_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,144 @@ s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw)
return 0;
}

/**
* ixgbe_fc_autoneg - Configure flow control
* @hw: pointer to hardware structure
*
* Negotiates flow control capabilities with link partner using autoneg and
* applies the results.
**/
s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw)
{
s32 ret_val = 0;
u32 i, reg, pcs_anadv_reg, pcs_lpab_reg;

reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);

/*
* The possible values of fc.current_mode are:
* 0: Flow control is completely disabled
* 1: Rx flow control is enabled (we can receive pause frames,
* but not send pause frames).
* 2: Tx flow control is enabled (we can send pause frames but
* we do not support receiving pause frames).
* 3: Both Rx and Tx flow control (symmetric) are enabled.
* other: Invalid.
*/
switch (hw->fc.current_mode) {
case ixgbe_fc_none:
/* Flow control completely disabled by software override. */
reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE);
break;
case ixgbe_fc_rx_pause:
/*
* Rx Flow control is enabled and Tx Flow control is
* disabled by software override. Since there really
* isn't a way to advertise that we are capable of RX
* Pause ONLY, we will advertise that we support both
* symmetric and asymmetric Rx PAUSE. Later, we will
* disable the adapter's ability to send PAUSE frames.
*/
reg |= (IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE);
break;
case ixgbe_fc_tx_pause:
/*
* Tx Flow control is enabled, and Rx Flow control is
* disabled by software override.
*/
reg |= (IXGBE_PCS1GANA_ASM_PAUSE);
reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE);
break;
case ixgbe_fc_full:
/* Flow control (both Rx and Tx) is enabled by SW override. */
reg |= (IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE);
break;
default:
hw_dbg(hw, "Flow control param set incorrectly\n");
ret_val = -IXGBE_ERR_CONFIG;
goto out;
break;
}

IXGBE_WRITE_REG(hw, IXGBE_PCS1GANA, reg);
reg = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL);

/* Set PCS register for autoneg */
/* Enable and restart autoneg */
reg |= IXGBE_PCS1GLCTL_AN_ENABLE | IXGBE_PCS1GLCTL_AN_RESTART;

/* Disable AN timeout */
if (hw->fc.strict_ieee)
reg &= ~IXGBE_PCS1GLCTL_AN_1G_TIMEOUT_EN;

hw_dbg(hw, "Configuring Autoneg; PCS_LCTL = 0x%08X\n", reg);
IXGBE_WRITE_REG(hw, IXGBE_PCS1GLCTL, reg);

/* See if autonegotiation has succeeded */
hw->mac.autoneg_succeeded = 0;
for (i = 0; i < FIBER_LINK_UP_LIMIT; i++) {
msleep(10);
reg = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);
if ((reg & (IXGBE_PCS1GLSTA_LINK_OK |
IXGBE_PCS1GLSTA_AN_COMPLETE)) ==
(IXGBE_PCS1GLSTA_LINK_OK |
IXGBE_PCS1GLSTA_AN_COMPLETE)) {
if (!(reg & IXGBE_PCS1GLSTA_AN_TIMED_OUT))
hw->mac.autoneg_succeeded = 1;
break;
}
}

if (!hw->mac.autoneg_succeeded) {
/* Autoneg failed to achieve a link, so we turn fc off */
hw->fc.current_mode = ixgbe_fc_none;
hw_dbg(hw, "Flow Control = NONE.\n");
goto out;
}

/*
* Read the AN advertisement and LP ability registers and resolve
* local flow control settings accordingly
*/
pcs_anadv_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
pcs_lpab_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP);
if ((pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) &&
(pcs_lpab_reg & IXGBE_PCS1GANA_SYM_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 == ixgbe_fc_full) {
hw->fc.current_mode = ixgbe_fc_full;
hw_dbg(hw, "Flow Control = FULL.\n");
} else {
hw->fc.current_mode = ixgbe_fc_rx_pause;
hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n");
}
} else if (!(pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) &&
(pcs_anadv_reg & IXGBE_PCS1GANA_ASM_PAUSE) &&
(pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE) &&
(pcs_lpab_reg & IXGBE_PCS1GANA_ASM_PAUSE)) {
hw->fc.current_mode = ixgbe_fc_tx_pause;
hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n");
} else if ((pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) &&
(pcs_anadv_reg & IXGBE_PCS1GANA_ASM_PAUSE) &&
!(pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE) &&
(pcs_lpab_reg & IXGBE_PCS1GANA_ASM_PAUSE)) {
hw->fc.current_mode = ixgbe_fc_rx_pause;
hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n");
} else {
hw->fc.current_mode = ixgbe_fc_none;
hw_dbg(hw, "Flow Control = NONE.\n");
}

out:
return ret_val;
}

/**
* ixgbe_disable_pcie_master - Disable PCI-express master access
* @hw: pointer to hardware structure
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/ixgbe/ixgbe_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ s32 ixgbe_update_uc_addr_list_generic(struct ixgbe_hw *hw, u8 *addr_list,
u32 addr_count, ixgbe_mc_addr_itr func);
s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw);
s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw);
s32 ixgbe_setup_fc_generic(struct ixgbe_hw *hw, s32 packetbuf_num);
s32 ixgbe_fc_enable(struct ixgbe_hw *hw, s32 packtetbuf_num);
s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw);

s32 ixgbe_validate_mac_addr(u8 *mac_addr);
s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask);
Expand Down
Loading

0 comments on commit 0ecc061

Please sign in to comment.