Commit 0ea065e5 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

sky2: fix pause negotiation

The sky2 driver combines auto speed negotiation with automatic negotiation
of pause parameters; but the ethtool interface expects them to be
split. This patch allows autonegotiation to be used for speed, but
manually disable flow control.
Signed-off-by: default avatarStephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 481cea4a
...@@ -321,7 +321,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -321,7 +321,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
struct sky2_port *sky2 = netdev_priv(hw->dev[port]); struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg; u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg;
if (sky2->autoneg == AUTONEG_ENABLE && if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) &&
!(hw->flags & SKY2_HW_NEWER_PHY)) { !(hw->flags & SKY2_HW_NEWER_PHY)) {
u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL); u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
...@@ -363,7 +363,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -363,7 +363,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO); ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO);
/* downshift on PHY 88E1112 and 88E1149 is changed */ /* downshift on PHY 88E1112 and 88E1149 is changed */
if (sky2->autoneg == AUTONEG_ENABLE if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED)
&& (hw->flags & SKY2_HW_NEWER_PHY)) { && (hw->flags & SKY2_HW_NEWER_PHY)) {
/* set downshift counter to 3x and enable downshift */ /* set downshift counter to 3x and enable downshift */
ctrl &= ~PHY_M_PC_DSC_MSK; ctrl &= ~PHY_M_PC_DSC_MSK;
...@@ -408,7 +408,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -408,7 +408,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
adv = PHY_AN_CSMA; adv = PHY_AN_CSMA;
reg = 0; reg = 0;
if (sky2->autoneg == AUTONEG_ENABLE) { if (sky2->flags & SKY2_FLAG_AUTO_SPEED) {
if (sky2_is_copper(hw)) { if (sky2_is_copper(hw)) {
if (sky2->advertising & ADVERTISED_1000baseT_Full) if (sky2->advertising & ADVERTISED_1000baseT_Full)
ct1000 |= PHY_M_1000C_AFD; ct1000 |= PHY_M_1000C_AFD;
...@@ -423,14 +423,11 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -423,14 +423,11 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
if (sky2->advertising & ADVERTISED_10baseT_Half) if (sky2->advertising & ADVERTISED_10baseT_Half)
adv |= PHY_M_AN_10_HD; adv |= PHY_M_AN_10_HD;
adv |= copper_fc_adv[sky2->flow_mode];
} else { /* special defines for FIBER (88E1040S only) */ } else { /* special defines for FIBER (88E1040S only) */
if (sky2->advertising & ADVERTISED_1000baseT_Full) if (sky2->advertising & ADVERTISED_1000baseT_Full)
adv |= PHY_M_AN_1000X_AFD; adv |= PHY_M_AN_1000X_AFD;
if (sky2->advertising & ADVERTISED_1000baseT_Half) if (sky2->advertising & ADVERTISED_1000baseT_Half)
adv |= PHY_M_AN_1000X_AHD; adv |= PHY_M_AN_1000X_AHD;
adv |= fiber_fc_adv[sky2->flow_mode];
} }
/* Restart Auto-negotiation */ /* Restart Auto-negotiation */
...@@ -439,8 +436,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -439,8 +436,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
/* forced speed/duplex settings */ /* forced speed/duplex settings */
ct1000 = PHY_M_1000C_MSE; ct1000 = PHY_M_1000C_MSE;
/* Disable auto update for duplex flow control and speed */ /* Disable auto update for duplex flow control and duplex */
reg |= GM_GPCR_AU_ALL_DIS; reg |= GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_SPD_DIS;
switch (sky2->speed) { switch (sky2->speed) {
case SPEED_1000: case SPEED_1000:
...@@ -458,8 +455,15 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -458,8 +455,15 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
ctrl |= PHY_CT_DUP_MD; ctrl |= PHY_CT_DUP_MD;
} else if (sky2->speed < SPEED_1000) } else if (sky2->speed < SPEED_1000)
sky2->flow_mode = FC_NONE; sky2->flow_mode = FC_NONE;
}
if (sky2->flags & SKY2_FLAG_AUTO_PAUSE) {
if (sky2_is_copper(hw))
adv |= copper_fc_adv[sky2->flow_mode];
else
adv |= fiber_fc_adv[sky2->flow_mode];
} else {
reg |= GM_GPCR_AU_FCT_DIS;
reg |= gm_fc_disable[sky2->flow_mode]; reg |= gm_fc_disable[sky2->flow_mode];
/* Forward pause packets to GMAC? */ /* Forward pause packets to GMAC? */
...@@ -594,7 +598,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -594,7 +598,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
/* no effect on Yukon-XL */ /* no effect on Yukon-XL */
gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
if (sky2->autoneg == AUTONEG_DISABLE || sky2->speed == SPEED_100) { if ( !(sky2->flags & SKY2_FLAG_AUTO_SPEED)
|| sky2->speed == SPEED_100) {
/* turn on 100 Mbps LED (LED_LINK100) */ /* turn on 100 Mbps LED (LED_LINK100) */
ledover |= PHY_M_LED_MO_100(MO_LED_ON); ledover |= PHY_M_LED_MO_100(MO_LED_ON);
} }
...@@ -605,7 +610,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ...@@ -605,7 +610,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
} }
/* Enable phy interrupt on auto-negotiation complete (or link up) */ /* Enable phy interrupt on auto-negotiation complete (or link up) */
if (sky2->autoneg == AUTONEG_ENABLE) if (sky2->flags & SKY2_FLAG_AUTO_SPEED)
gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL); gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL);
else else
gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK); gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
...@@ -661,7 +666,9 @@ static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port) ...@@ -661,7 +666,9 @@ static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port)
/* setup General Purpose Control Register */ /* setup General Purpose Control Register */
gma_write16(hw, port, GM_GP_CTRL, gma_write16(hw, port, GM_GP_CTRL,
GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 | GM_GPCR_AU_ALL_DIS); GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 |
GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS |
GM_GPCR_AU_SPD_DIS);
if (hw->chip_id != CHIP_ID_YUKON_EC) { if (hw->chip_id != CHIP_ID_YUKON_EC) {
if (hw->chip_id == CHIP_ID_YUKON_EC_U) { if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
...@@ -1117,7 +1124,8 @@ static void rx_set_checksum(struct sky2_port *sky2) ...@@ -1117,7 +1124,8 @@ static void rx_set_checksum(struct sky2_port *sky2)
sky2_write32(sky2->hw, sky2_write32(sky2->hw,
Q_ADDR(rxqaddr[sky2->port], Q_CSR), Q_ADDR(rxqaddr[sky2->port], Q_CSR),
sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); (sky2->flags & SKY2_FLAG_RX_CHECKSUM)
? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
} }
/* /*
...@@ -2087,7 +2095,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) ...@@ -2087,7 +2095,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", printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n",
sky2->netdev->name, istatus, phystat); sky2->netdev->name, istatus, phystat);
if (sky2->autoneg == AUTONEG_ENABLE && (istatus & PHY_M_IS_AN_COMPL)) { if (istatus & PHY_M_IS_AN_COMPL) {
if (sky2_autoneg_done(sky2, phystat) == 0) if (sky2_autoneg_done(sky2, phystat) == 0)
sky2_link_up(sky2); sky2_link_up(sky2);
goto out; goto out;
...@@ -2453,7 +2461,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) ...@@ -2453,7 +2461,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
/* This chip reports checksum status differently */ /* This chip reports checksum status differently */
if (hw->flags & SKY2_HW_NEW_LE) { if (hw->flags & SKY2_HW_NEW_LE) {
if (sky2->rx_csum && if ((sky2->flags & SKY2_FLAG_RX_CHECKSUM) &&
(le->css & (CSS_ISIPV4 | CSS_ISIPV6)) && (le->css & (CSS_ISIPV4 | CSS_ISIPV6)) &&
(le->css & CSS_TCPUDPCSOK)) (le->css & CSS_TCPUDPCSOK))
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->ip_summed = CHECKSUM_UNNECESSARY;
...@@ -2480,7 +2488,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) ...@@ -2480,7 +2488,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
/* fall through */ /* fall through */
#endif #endif
case OP_RXCHKS: case OP_RXCHKS:
if (!sky2->rx_csum) if (!(sky2->flags & SKY2_FLAG_RX_CHECKSUM))
break; break;
/* If this happens then driver assuming wrong format */ /* If this happens then driver assuming wrong format */
...@@ -2505,7 +2513,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) ...@@ -2505,7 +2513,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
printk(KERN_NOTICE PFX "%s: hardware receive " printk(KERN_NOTICE PFX "%s: hardware receive "
"checksum problem (status = %#x)\n", "checksum problem (status = %#x)\n",
dev->name, status); dev->name, status);
sky2->rx_csum = 0; sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
sky2_write32(sky2->hw, sky2_write32(sky2->hw,
Q_ADDR(rxqaddr[port], Q_CSR), Q_ADDR(rxqaddr[port], Q_CSR),
BMU_DIS_RX_CHKSUM); BMU_DIS_RX_CHKSUM);
...@@ -3206,7 +3215,8 @@ static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ...@@ -3206,7 +3215,8 @@ static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
} }
ecmd->advertising = sky2->advertising; ecmd->advertising = sky2->advertising;
ecmd->autoneg = sky2->autoneg; ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_SPEED)
? AUTONEG_ENABLE : AUTONEG_DISABLE;
ecmd->duplex = sky2->duplex; ecmd->duplex = sky2->duplex;
return 0; return 0;
} }
...@@ -3218,6 +3228,7 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ...@@ -3218,6 +3228,7 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
u32 supported = sky2_supported_modes(hw); u32 supported = sky2_supported_modes(hw);
if (ecmd->autoneg == AUTONEG_ENABLE) { if (ecmd->autoneg == AUTONEG_ENABLE) {
sky2->flags |= SKY2_FLAG_AUTO_SPEED;
ecmd->advertising = supported; ecmd->advertising = supported;
sky2->duplex = -1; sky2->duplex = -1;
sky2->speed = -1; sky2->speed = -1;
...@@ -3259,9 +3270,9 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ...@@ -3259,9 +3270,9 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
sky2->speed = ecmd->speed; sky2->speed = ecmd->speed;
sky2->duplex = ecmd->duplex; sky2->duplex = ecmd->duplex;
sky2->flags &= ~SKY2_FLAG_AUTO_SPEED;
} }
sky2->autoneg = ecmd->autoneg;
sky2->advertising = ecmd->advertising; sky2->advertising = ecmd->advertising;
if (netif_running(dev)) { if (netif_running(dev)) {
...@@ -3331,14 +3342,17 @@ static u32 sky2_get_rx_csum(struct net_device *dev) ...@@ -3331,14 +3342,17 @@ static u32 sky2_get_rx_csum(struct net_device *dev)
{ {
struct sky2_port *sky2 = netdev_priv(dev); struct sky2_port *sky2 = netdev_priv(dev);
return sky2->rx_csum; return !!(sky2->flags & SKY2_FLAG_RX_CHECKSUM);
} }
static int sky2_set_rx_csum(struct net_device *dev, u32 data) static int sky2_set_rx_csum(struct net_device *dev, u32 data)
{ {
struct sky2_port *sky2 = netdev_priv(dev); struct sky2_port *sky2 = netdev_priv(dev);
sky2->rx_csum = data; if (data)
sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
else
sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR), sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
...@@ -3356,7 +3370,7 @@ static int sky2_nway_reset(struct net_device *dev) ...@@ -3356,7 +3370,7 @@ static int sky2_nway_reset(struct net_device *dev)
{ {
struct sky2_port *sky2 = netdev_priv(dev); struct sky2_port *sky2 = netdev_priv(dev);
if (!netif_running(dev) || sky2->autoneg != AUTONEG_ENABLE) if (!netif_running(dev) || !(sky2->flags & SKY2_FLAG_AUTO_SPEED))
return -EINVAL; return -EINVAL;
sky2_phy_reinit(sky2); sky2_phy_reinit(sky2);
...@@ -3596,7 +3610,8 @@ static void sky2_get_pauseparam(struct net_device *dev, ...@@ -3596,7 +3610,8 @@ static void sky2_get_pauseparam(struct net_device *dev,
ecmd->tx_pause = ecmd->rx_pause = 1; ecmd->tx_pause = ecmd->rx_pause = 1;
} }
ecmd->autoneg = sky2->autoneg; ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_PAUSE)
? AUTONEG_ENABLE : AUTONEG_DISABLE;
} }
static int sky2_set_pauseparam(struct net_device *dev, static int sky2_set_pauseparam(struct net_device *dev,
...@@ -3604,7 +3619,11 @@ static int sky2_set_pauseparam(struct net_device *dev, ...@@ -3604,7 +3619,11 @@ static int sky2_set_pauseparam(struct net_device *dev,
{ {
struct sky2_port *sky2 = netdev_priv(dev); struct sky2_port *sky2 = netdev_priv(dev);
sky2->autoneg = ecmd->autoneg; if (ecmd->autoneg == AUTONEG_ENABLE)
sky2->flags |= SKY2_FLAG_AUTO_PAUSE;
else
sky2->flags &= ~SKY2_FLAG_AUTO_PAUSE;
sky2->flow_mode = sky2_flow(ecmd->rx_pause, ecmd->tx_pause); sky2->flow_mode = sky2_flow(ecmd->rx_pause, ecmd->tx_pause);
if (netif_running(dev)) if (netif_running(dev))
...@@ -4294,13 +4313,15 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, ...@@ -4294,13 +4313,15 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
sky2->msg_enable = netif_msg_init(debug, default_msg); sky2->msg_enable = netif_msg_init(debug, default_msg);
/* Auto speed and flow control */ /* Auto speed and flow control */
sky2->autoneg = AUTONEG_ENABLE; sky2->flags = SKY2_FLAG_AUTO_SPEED | SKY2_FLAG_AUTO_PAUSE;
if (hw->chip_id != CHIP_ID_YUKON_XL)
sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
sky2->flow_mode = FC_BOTH; sky2->flow_mode = FC_BOTH;
sky2->duplex = -1; sky2->duplex = -1;
sky2->speed = -1; sky2->speed = -1;
sky2->advertising = sky2_supported_modes(hw); sky2->advertising = sky2_supported_modes(hw);
sky2->rx_csum = (hw->chip_id != CHIP_ID_YUKON_XL);
sky2->wol = wol; sky2->wol = wol;
spin_lock_init(&sky2->phy_lock); spin_lock_init(&sky2->phy_lock);
......
...@@ -1583,7 +1583,6 @@ enum { ...@@ -1583,7 +1583,6 @@ enum {
}; };
#define GM_GPCR_SPEED_1000 (GM_GPCR_GIGS_ENA | GM_GPCR_SPEED_100) #define GM_GPCR_SPEED_1000 (GM_GPCR_GIGS_ENA | GM_GPCR_SPEED_100)
#define GM_GPCR_AU_ALL_DIS (GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS|GM_GPCR_AU_SPD_DIS)
/* GM_TX_CTRL 16 bit r/w Transmit Control Register */ /* GM_TX_CTRL 16 bit r/w Transmit Control Register */
enum { enum {
...@@ -2042,15 +2041,18 @@ struct sky2_port { ...@@ -2042,15 +2041,18 @@ struct sky2_port {
u8 fifo_lev; u8 fifo_lev;
} check; } check;
dma_addr_t rx_le_map; dma_addr_t rx_le_map;
dma_addr_t tx_le_map; dma_addr_t tx_le_map;
u16 advertising; /* ADVERTISED_ bits */ u16 advertising; /* ADVERTISED_ bits */
u16 speed; /* SPEED_1000, SPEED_100, ... */ u16 speed; /* SPEED_1000, SPEED_100, ... */
u8 autoneg; /* AUTONEG_ENABLE, AUTONEG_DISABLE */ u8 wol; /* WAKE_ bits */
u8 duplex; /* DUPLEX_HALF, DUPLEX_FULL */ u8 duplex; /* DUPLEX_HALF, DUPLEX_FULL */
u8 rx_csum; u16 flags;
u8 wol; #define SKY2_FLAG_RX_CHECKSUM 0x0001
#define SKY2_FLAG_AUTO_SPEED 0x0002
#define SKY2_FLAG_AUTO_PAUSE 0x0004
u8 restarting; u8 restarting;
enum flow_control flow_mode; enum flow_control flow_mode;
enum flow_control flow_status; enum flow_control flow_status;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment