Skip to content

Commit

Permalink
[PATCH] sky2: tx pause bug fix
Browse files Browse the repository at this point in the history
Fix problems with transmit pause frames. The driver was telling the
GMAC to flush (not process) pause frames. Manually disabling pause wasn't
working because of problems in the setup.

This maybe the cause of the lockup under load.
	http://bugzilla.kernel.org/show_bug.cgi?id=6839

Patch against netdev-2.6 git tree

Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
  • Loading branch information
Stephen Hemminger authored and Jeff Garzik committed Sep 13, 2006
1 parent 6aa20a2 commit 2eaba1a
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 82 deletions.
123 changes: 42 additions & 81 deletions drivers/net/sky2.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port)
static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
{
struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
u16 ctrl, ct1000, adv, pg, ledctrl, ledover;
u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg;

if (sky2->autoneg == AUTONEG_ENABLE &&
!(hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)) {
Expand Down Expand Up @@ -358,6 +358,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
ctrl = 0;
ct1000 = 0;
adv = PHY_AN_CSMA;
reg = 0;

if (sky2->autoneg == AUTONEG_ENABLE) {
if (hw->copper) {
Expand Down Expand Up @@ -390,21 +391,46 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
/* forced speed/duplex settings */
ct1000 = PHY_M_1000C_MSE;

if (sky2->duplex == DUPLEX_FULL)
ctrl |= PHY_CT_DUP_MD;
/* Disable auto update for duplex flow control and speed */
reg |= GM_GPCR_AU_ALL_DIS;

switch (sky2->speed) {
case SPEED_1000:
ctrl |= PHY_CT_SP1000;
reg |= GM_GPCR_SPEED_1000;
break;
case SPEED_100:
ctrl |= PHY_CT_SP100;
reg |= GM_GPCR_SPEED_100;
break;
}

if (sky2->duplex == DUPLEX_FULL) {
reg |= GM_GPCR_DUP_FULL;
ctrl |= PHY_CT_DUP_MD;
} else if (sky2->speed != SPEED_1000 && hw->chip_id != CHIP_ID_YUKON_EC_U) {
/* Turn off flow control for 10/100mbps */
sky2->rx_pause = 0;
sky2->tx_pause = 0;
}

if (!sky2->rx_pause)
reg |= GM_GPCR_FC_RX_DIS;

if (!sky2->tx_pause)
reg |= GM_GPCR_FC_TX_DIS;

/* Forward pause packets to GMAC? */
if (sky2->tx_pause || sky2->rx_pause)
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
else
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);

ctrl |= PHY_CT_RESET;
}

gma_write16(hw, port, GM_GP_CTRL, reg);

if (hw->chip_id != CHIP_ID_YUKON_FE)
gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, ct1000);

Expand Down Expand Up @@ -508,6 +534,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);

}

/* Enable phy interrupt on auto-negotiation complete (or link up) */
if (sky2->autoneg == AUTONEG_ENABLE)
gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL);
Expand Down Expand Up @@ -570,49 +597,11 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
gm_phy_read(hw, 1, PHY_MARV_INT_MASK) != 0);
}

if (sky2->autoneg == AUTONEG_DISABLE) {
reg = gma_read16(hw, port, GM_GP_CTRL);
reg |= GM_GPCR_AU_ALL_DIS;
gma_write16(hw, port, GM_GP_CTRL, reg);
gma_read16(hw, port, GM_GP_CTRL);

switch (sky2->speed) {
case SPEED_1000:
reg &= ~GM_GPCR_SPEED_100;
reg |= GM_GPCR_SPEED_1000;
break;
case SPEED_100:
reg &= ~GM_GPCR_SPEED_1000;
reg |= GM_GPCR_SPEED_100;
break;
case SPEED_10:
reg &= ~(GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100);
break;
}

if (sky2->duplex == DUPLEX_FULL)
reg |= GM_GPCR_DUP_FULL;

/* turn off pause in 10/100mbps half duplex */
else if (sky2->speed != SPEED_1000 &&
hw->chip_id != CHIP_ID_YUKON_EC_U)
sky2->tx_pause = sky2->rx_pause = 0;
} else
reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL;

if (!sky2->tx_pause && !sky2->rx_pause) {
sky2_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
reg |=
GM_GPCR_FC_TX_DIS | GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS;
} else if (sky2->tx_pause && !sky2->rx_pause) {
/* disable Rx flow-control */
reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS;
}

gma_write16(hw, port, GM_GP_CTRL, reg);

sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC));

/* Enable Transmit FIFO Underrun */
sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);

spin_lock_bh(&sky2->phy_lock);
sky2_phy_init(hw, port);
spin_unlock_bh(&sky2->phy_lock);
Expand Down Expand Up @@ -1529,40 +1518,10 @@ static void sky2_link_up(struct sky2_port *sky2)
unsigned port = sky2->port;
u16 reg;

/* Enable Transmit FIFO Underrun */
sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);

reg = gma_read16(hw, port, GM_GP_CTRL);
if (sky2->autoneg == AUTONEG_DISABLE) {
reg |= GM_GPCR_AU_ALL_DIS;

/* Is write/read necessary? Copied from sky2_mac_init */
gma_write16(hw, port, GM_GP_CTRL, reg);
gma_read16(hw, port, GM_GP_CTRL);

switch (sky2->speed) {
case SPEED_1000:
reg &= ~GM_GPCR_SPEED_100;
reg |= GM_GPCR_SPEED_1000;
break;
case SPEED_100:
reg &= ~GM_GPCR_SPEED_1000;
reg |= GM_GPCR_SPEED_100;
break;
case SPEED_10:
reg &= ~(GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100);
break;
}
} else
reg &= ~GM_GPCR_AU_ALL_DIS;

if (sky2->duplex == DUPLEX_FULL || sky2->autoneg == AUTONEG_ENABLE)
reg |= GM_GPCR_DUP_FULL;

/* enable Rx/Tx */
reg = gma_read16(hw, port, GM_GP_CTRL);
reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
gma_write16(hw, port, GM_GP_CTRL, reg);
gma_read16(hw, port, GM_GP_CTRL);

gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);

Expand Down Expand Up @@ -1616,7 +1575,6 @@ static void sky2_link_down(struct sky2_port *sky2)
reg = gma_read16(hw, port, GM_GP_CTRL);
reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
gma_write16(hw, port, GM_GP_CTRL, reg);
gma_read16(hw, port, GM_GP_CTRL); /* PCI post */

if (sky2->rx_pause && !sky2->tx_pause) {
/* restore Asymmetric Pause bit */
Expand All @@ -1633,6 +1591,7 @@ static void sky2_link_down(struct sky2_port *sky2)

if (netif_msg_link(sky2))
printk(KERN_INFO PFX "%s: Link is down.\n", sky2->netdev->name);

sky2_phy_init(hw, port);
}

Expand Down Expand Up @@ -1673,8 +1632,11 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0;
sky2->tx_pause = (aux & PHY_M_PS_TX_P_EN) != 0;

if ((sky2->tx_pause || sky2->rx_pause)
&& !(sky2->speed < SPEED_1000 && sky2->duplex == DUPLEX_HALF))
if (sky2->duplex == DUPLEX_HALF && sky2->speed != SPEED_1000
&& hw->chip_id != CHIP_ID_YUKON_EC_U)
sky2->rx_pause = sky2->tx_pause = 0;

if (sky2->rx_pause || sky2->tx_pause)
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
else
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
Expand All @@ -1700,7 +1662,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port)
printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n",
sky2->netdev->name, istatus, phystat);

if (istatus & PHY_M_IS_AN_COMPL) {
if (sky2->autoneg == AUTONEG_ENABLE && (istatus & PHY_M_IS_AN_COMPL)) {
if (sky2_autoneg_done(sky2, phystat) == 0)
sky2_link_up(sky2);
goto out;
Expand Down Expand Up @@ -2890,15 +2852,14 @@ static int sky2_set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *ecmd)
{
struct sky2_port *sky2 = netdev_priv(dev);
int err = 0;

sky2->autoneg = ecmd->autoneg;
sky2->tx_pause = ecmd->tx_pause != 0;
sky2->rx_pause = ecmd->rx_pause != 0;

sky2_phy_reinit(sky2);

return err;
return 0;
}

static int sky2_get_coalesce(struct net_device *dev,
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/sky2.h
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ enum {

GMR_FS_ANY_ERR = GMR_FS_RX_FF_OV | GMR_FS_CRC_ERR |
GMR_FS_FRAGMENT | GMR_FS_LONG_ERR |
GMR_FS_MII_ERR | GMR_FS_BAD_FC | GMR_FS_GOOD_FC |
GMR_FS_MII_ERR | GMR_FS_BAD_FC |
GMR_FS_UN_SIZE | GMR_FS_JABBER,
};

Expand Down

0 comments on commit 2eaba1a

Please sign in to comment.