Skip to content

Commit

Permalink
ixgbe: Fix backplane flow control autoneg
Browse files Browse the repository at this point in the history
Backplane flow control autonegotiation is currently broken for
ixgbe devices.  This patch fixes the flow control issues
with clause 37 autoneg.

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 Oct 1, 2009
1 parent 84f62d4 commit 539e5f0
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 52 deletions.
2 changes: 1 addition & 1 deletion drivers/net/ixgbe/ixgbe_82598.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
#endif /* CONFIG_DCB */
default:
hw_dbg(hw, "Flow control param set incorrectly\n");
ret_val = -IXGBE_ERR_CONFIG;
ret_val = IXGBE_ERR_CONFIG;
goto out;
break;
}
Expand Down
228 changes: 177 additions & 51 deletions drivers/net/ixgbe/ixgbe_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1663,7 +1663,7 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
#endif /* CONFIG_DCB */
default:
hw_dbg(hw, "Flow control param set incorrectly\n");
ret_val = -IXGBE_ERR_CONFIG;
ret_val = IXGBE_ERR_CONFIG;
goto out;
break;
}
Expand Down Expand Up @@ -1734,75 +1734,140 @@ s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw)
s32 ret_val = 0;
ixgbe_link_speed speed;
u32 pcs_anadv_reg, pcs_lpab_reg, linkstat;
u32 links2, anlp1_reg, autoc_reg, links;
bool link_up;

/*
* AN should have completed when the cable was plugged in.
* Look for reasons to bail out. Bail out if:
* - FC autoneg is disabled, or if
* - we don't have multispeed fiber, or if
* - we're not running at 1G, or if
* - link is not up, or if
* - link is up but AN did not complete, or if
* - link is up and AN completed but timed out
* - link is not up.
*
* Since we're being called from an LSC, link is already know to be up.
* Since we're being called from an LSC, link is already known to be up.
* So use link_up_wait_to_complete=false.
*/
hw->mac.ops.check_link(hw, &speed, &link_up, false);
linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);

if (hw->fc.disable_fc_autoneg ||
!hw->phy.multispeed_fiber ||
(speed != IXGBE_LINK_SPEED_1GB_FULL) ||
!link_up ||
((linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) ||
((linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) {

if (hw->fc.disable_fc_autoneg || (!link_up)) {
hw->fc.fc_was_autonegged = false;
hw->fc.current_mode = hw->fc.requested_mode;
hw_dbg(hw, "Autoneg FC was skipped.\n");
goto out;
}

/*
* On backplane, bail out if
* - backplane autoneg was not completed, or if
* - link partner is not AN enabled
*/
if (hw->phy.media_type == ixgbe_media_type_backplane) {
links = IXGBE_READ_REG(hw, IXGBE_LINKS);
links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2);
if (((links & IXGBE_LINKS_KX_AN_COMP) == 0) ||
((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0)) {
hw->fc.fc_was_autonegged = false;
hw->fc.current_mode = hw->fc.requested_mode;
goto out;
}
}

/*
* On multispeed fiber at 1g, bail out if
* - link is up but AN did not complete, or if
* - link is up and AN completed but timed out
*/
if (hw->phy.multispeed_fiber && (speed == IXGBE_LINK_SPEED_1GB_FULL)) {
linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);
if (((linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) ||
((linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) {
hw->fc.fc_was_autonegged = false;
hw->fc.current_mode = hw->fc.requested_mode;
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)) {
if ((speed == IXGBE_LINK_SPEED_1GB_FULL) &&
(hw->phy.media_type != ixgbe_media_type_backplane)) {
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 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");
}
}

if (hw->phy.media_type == ixgbe_media_type_backplane) {
/*
* 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.
* Read the 10g AN autoc and LP ability registers and resolve
* local flow control settings accordingly
*/
if (hw->fc.requested_mode == ixgbe_fc_full) {
hw->fc.current_mode = ixgbe_fc_full;
hw_dbg(hw, "Flow Control = FULL.\n");
} else {
autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
anlp1_reg = IXGBE_READ_REG(hw, IXGBE_ANLP1);

if ((autoc_reg & IXGBE_AUTOC_SYM_PAUSE) &&
(anlp1_reg & IXGBE_ANLP1_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 only\n");
}
} else if (!(autoc_reg & IXGBE_AUTOC_SYM_PAUSE) &&
(autoc_reg & IXGBE_AUTOC_ASM_PAUSE) &&
(anlp1_reg & IXGBE_ANLP1_SYM_PAUSE) &&
(anlp1_reg & IXGBE_ANLP1_ASM_PAUSE)) {
hw->fc.current_mode = ixgbe_fc_tx_pause;
hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n");
} else if ((autoc_reg & IXGBE_AUTOC_SYM_PAUSE) &&
(autoc_reg & IXGBE_AUTOC_ASM_PAUSE) &&
!(anlp1_reg & IXGBE_ANLP1_SYM_PAUSE) &&
(anlp1_reg & IXGBE_ANLP1_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");
}
} 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");
}

/* Record that current_mode is the result of a successful autoneg */
hw->fc.fc_was_autonegged = true;

Expand Down Expand Up @@ -1919,24 +1984,85 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num)
#endif /* CONFIG_DCB */
default:
hw_dbg(hw, "Flow control param set incorrectly\n");
ret_val = -IXGBE_ERR_CONFIG;
ret_val = IXGBE_ERR_CONFIG;
goto out;
break;
}

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

/* Enable and restart autoneg to inform the link partner */
reg |= IXGBE_PCS1GLCTL_AN_ENABLE | IXGBE_PCS1GLCTL_AN_RESTART;

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

IXGBE_WRITE_REG(hw, IXGBE_PCS1GLCTL, reg);
hw_dbg(hw, "Set up FC; PCS1GLCTL = 0x%08X\n", reg);

/*
* Set up the 10G flow control advertisement registers so the HW
* can do fc autoneg once the cable is plugged in. If we end up
* using 1g instead, this is harmless.
*/
reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);

/*
* The possible values of fc.requested_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.requested_mode) {
case ixgbe_fc_none:
/* Flow control completely disabled by software override. */
reg &= ~(IXGBE_AUTOC_SYM_PAUSE | IXGBE_AUTOC_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_AUTOC_SYM_PAUSE | IXGBE_AUTOC_ASM_PAUSE);
break;
case ixgbe_fc_tx_pause:
/*
* Tx Flow control is enabled, and Rx Flow control is
* disabled by software override.
*/
reg |= (IXGBE_AUTOC_ASM_PAUSE);
reg &= ~(IXGBE_AUTOC_SYM_PAUSE);
break;
case ixgbe_fc_full:
/* Flow control (both Rx and Tx) is enabled by SW override. */
reg |= (IXGBE_AUTOC_SYM_PAUSE | IXGBE_AUTOC_ASM_PAUSE);
break;
#ifdef CONFIG_DCB
case ixgbe_fc_pfc:
goto out;
break;
#endif /* CONFIG_DCB */
default:
hw_dbg(hw, "Flow control param set incorrectly\n");
ret_val = IXGBE_ERR_CONFIG;
goto out;
break;
}
/*
* AUTOC restart handles negotiation of 1G and 10G. There is
* no need to set the PCS1GCTL register.
*/
reg |= IXGBE_AUTOC_AN_RESTART;
IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg);
hw_dbg(hw, "Set up FC; IXGBE_AUTOC = 0x%08X\n", reg);

out:
return ret_val;
}
Expand Down Expand Up @@ -2000,7 +2126,7 @@ s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask)

while (timeout) {
if (ixgbe_get_eeprom_semaphore(hw))
return -IXGBE_ERR_SWFW_SYNC;
return IXGBE_ERR_SWFW_SYNC;

gssr = IXGBE_READ_REG(hw, IXGBE_GSSR);
if (!(gssr & (fwmask | swmask)))
Expand All @@ -2017,7 +2143,7 @@ s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask)

if (!timeout) {
hw_dbg(hw, "Driver can't access resource, GSSR timeout.\n");
return -IXGBE_ERR_SWFW_SYNC;
return IXGBE_ERR_SWFW_SYNC;
}

gssr |= swmask;
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ixgbe/ixgbe_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,8 @@
#define IXGBE_AUTOC_KX4_SUPP 0x80000000
#define IXGBE_AUTOC_KX_SUPP 0x40000000
#define IXGBE_AUTOC_PAUSE 0x30000000
#define IXGBE_AUTOC_ASM_PAUSE 0x20000000
#define IXGBE_AUTOC_SYM_PAUSE 0x10000000
#define IXGBE_AUTOC_RF 0x08000000
#define IXGBE_AUTOC_PD_TMR 0x06000000
#define IXGBE_AUTOC_AN_RX_LOOSE 0x01000000
Expand Down Expand Up @@ -1404,6 +1406,8 @@
#define IXGBE_LINK_UP_TIME 90 /* 9.0 Seconds */
#define IXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */

#define IXGBE_LINKS2_AN_SUPPORTED 0x00000040

/* PCS1GLSTA Bit Masks */
#define IXGBE_PCS1GLSTA_LINK_OK 1
#define IXGBE_PCS1GLSTA_SYNK_OK 0x10
Expand All @@ -1424,6 +1428,11 @@
#define IXGBE_PCS1GLCTL_AN_ENABLE 0x10000
#define IXGBE_PCS1GLCTL_AN_RESTART 0x20000

/* ANLP1 Bit Masks */
#define IXGBE_ANLP1_PAUSE 0x0C00
#define IXGBE_ANLP1_SYM_PAUSE 0x0400
#define IXGBE_ANLP1_ASM_PAUSE 0x0800

/* SW Semaphore Register bitmasks */
#define IXGBE_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
#define IXGBE_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
Expand Down

0 comments on commit 539e5f0

Please sign in to comment.