[PATCH] skge: better flow control negotiation

Do flow control negotiation properly. Don't let auto negotiation
status limit renegotiation. Separate desired pause values from
the result of auto negotiation.

Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
Stephen Hemminger 2006-10-05 15:49:52 -07:00 committed by Jeff Garzik
parent 4b67be999e
commit 5d5c8e0378
2 changed files with 95 additions and 56 deletions

View File

@ -487,31 +487,37 @@ static void skge_get_pauseparam(struct net_device *dev,
{ {
struct skge_port *skge = netdev_priv(dev); struct skge_port *skge = netdev_priv(dev);
ecmd->tx_pause = (skge->flow_control == FLOW_MODE_LOC_SEND) ecmd->rx_pause = (skge->flow_control == FLOW_MODE_SYMMETRIC)
|| (skge->flow_control == FLOW_MODE_SYMMETRIC); || (skge->flow_control == FLOW_MODE_SYM_OR_REM);
ecmd->rx_pause = (skge->flow_control == FLOW_MODE_REM_SEND) ecmd->tx_pause = ecmd->rx_pause || (skge->flow_control == FLOW_MODE_LOC_SEND);
|| (skge->flow_control == FLOW_MODE_SYMMETRIC);
ecmd->autoneg = skge->autoneg; ecmd->autoneg = ecmd->rx_pause || ecmd->tx_pause;
} }
static int skge_set_pauseparam(struct net_device *dev, static int skge_set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *ecmd) struct ethtool_pauseparam *ecmd)
{ {
struct skge_port *skge = netdev_priv(dev); struct skge_port *skge = netdev_priv(dev);
struct ethtool_pauseparam old;
skge->autoneg = ecmd->autoneg; skge_get_pauseparam(dev, &old);
if (ecmd->rx_pause && ecmd->tx_pause)
skge->flow_control = FLOW_MODE_SYMMETRIC; if (ecmd->autoneg != old.autoneg)
else if (ecmd->rx_pause && !ecmd->tx_pause) skge->flow_control = ecmd->autoneg ? FLOW_MODE_NONE : FLOW_MODE_SYMMETRIC;
skge->flow_control = FLOW_MODE_REM_SEND; else {
else if (!ecmd->rx_pause && ecmd->tx_pause) if (ecmd->rx_pause && ecmd->tx_pause)
skge->flow_control = FLOW_MODE_LOC_SEND; skge->flow_control = FLOW_MODE_SYMMETRIC;
else else if (ecmd->rx_pause && !ecmd->tx_pause)
skge->flow_control = FLOW_MODE_NONE; skge->flow_control = FLOW_MODE_SYM_OR_REM;
else if (!ecmd->rx_pause && ecmd->tx_pause)
skge->flow_control = FLOW_MODE_LOC_SEND;
else
skge->flow_control = FLOW_MODE_NONE;
}
if (netif_running(dev)) if (netif_running(dev))
skge_phy_reset(skge); skge_phy_reset(skge);
return 0; return 0;
} }
@ -854,6 +860,23 @@ static int skge_rx_fill(struct net_device *dev)
return 0; return 0;
} }
static const char *skge_pause(enum pause_status status)
{
switch(status) {
case FLOW_STAT_NONE:
return "none";
case FLOW_STAT_REM_SEND:
return "rx only";
case FLOW_STAT_LOC_SEND:
return "tx_only";
case FLOW_STAT_SYMMETRIC: /* Both station may send PAUSE */
return "both";
default:
return "indeterminated";
}
}
static void skge_link_up(struct skge_port *skge) static void skge_link_up(struct skge_port *skge)
{ {
skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG),
@ -862,16 +885,13 @@ static void skge_link_up(struct skge_port *skge)
netif_carrier_on(skge->netdev); netif_carrier_on(skge->netdev);
netif_wake_queue(skge->netdev); netif_wake_queue(skge->netdev);
if (netif_msg_link(skge)) if (netif_msg_link(skge)) {
printk(KERN_INFO PFX printk(KERN_INFO PFX
"%s: Link is up at %d Mbps, %s duplex, flow control %s\n", "%s: Link is up at %d Mbps, %s duplex, flow control %s\n",
skge->netdev->name, skge->speed, skge->netdev->name, skge->speed,
skge->duplex == DUPLEX_FULL ? "full" : "half", skge->duplex == DUPLEX_FULL ? "full" : "half",
(skge->flow_control == FLOW_MODE_NONE) ? "none" : skge_pause(skge->flow_status));
(skge->flow_control == FLOW_MODE_LOC_SEND) ? "tx only" : }
(skge->flow_control == FLOW_MODE_REM_SEND) ? "rx only" :
(skge->flow_control == FLOW_MODE_SYMMETRIC) ? "tx and rx" :
"unknown");
} }
static void skge_link_down(struct skge_port *skge) static void skge_link_down(struct skge_port *skge)
@ -1015,7 +1035,7 @@ static const u16 phy_pause_map[] = {
[FLOW_MODE_NONE] = 0, [FLOW_MODE_NONE] = 0,
[FLOW_MODE_LOC_SEND] = PHY_AN_PAUSE_ASYM, [FLOW_MODE_LOC_SEND] = PHY_AN_PAUSE_ASYM,
[FLOW_MODE_SYMMETRIC] = PHY_AN_PAUSE_CAP, [FLOW_MODE_SYMMETRIC] = PHY_AN_PAUSE_CAP,
[FLOW_MODE_REM_SEND] = PHY_AN_PAUSE_CAP | PHY_AN_PAUSE_ASYM, [FLOW_MODE_SYM_OR_REM] = PHY_AN_PAUSE_CAP | PHY_AN_PAUSE_ASYM,
}; };
/* special defines for FIBER (88E1011S only) */ /* special defines for FIBER (88E1011S only) */
@ -1023,7 +1043,7 @@ static const u16 fiber_pause_map[] = {
[FLOW_MODE_NONE] = PHY_X_P_NO_PAUSE, [FLOW_MODE_NONE] = PHY_X_P_NO_PAUSE,
[FLOW_MODE_LOC_SEND] = PHY_X_P_ASYM_MD, [FLOW_MODE_LOC_SEND] = PHY_X_P_ASYM_MD,
[FLOW_MODE_SYMMETRIC] = PHY_X_P_SYM_MD, [FLOW_MODE_SYMMETRIC] = PHY_X_P_SYM_MD,
[FLOW_MODE_REM_SEND] = PHY_X_P_BOTH_MD, [FLOW_MODE_SYM_OR_REM] = PHY_X_P_BOTH_MD,
}; };
@ -1072,20 +1092,19 @@ static void bcom_check_link(struct skge_hw *hw, int port)
return; return;
} }
/* We are using IEEE 802.3z/D5.0 Table 37-4 */ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
switch (aux & PHY_B_AS_PAUSE_MSK) { switch (aux & PHY_B_AS_PAUSE_MSK) {
case PHY_B_AS_PAUSE_MSK: case PHY_B_AS_PAUSE_MSK:
skge->flow_control = FLOW_MODE_SYMMETRIC; skge->flow_status = FLOW_STAT_SYMMETRIC;
break; break;
case PHY_B_AS_PRR: case PHY_B_AS_PRR:
skge->flow_control = FLOW_MODE_REM_SEND; skge->flow_status = FLOW_STAT_REM_SEND;
break; break;
case PHY_B_AS_PRT: case PHY_B_AS_PRT:
skge->flow_control = FLOW_MODE_LOC_SEND; skge->flow_status = FLOW_STAT_LOC_SEND;
break; break;
default: default:
skge->flow_control = FLOW_MODE_NONE; skge->flow_status = FLOW_STAT_NONE;
} }
skge->speed = SPEED_1000; skge->speed = SPEED_1000;
} }
@ -1283,15 +1302,20 @@ static void xm_check_link(struct net_device *dev)
} }
/* We are using IEEE 802.3z/D5.0 Table 37-4 */ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
if (lpa & PHY_X_P_SYM_MD) if ((skge->flow_control == FLOW_MODE_SYMMETRIC ||
skge->flow_control = FLOW_MODE_SYMMETRIC; skge->flow_control == FLOW_MODE_SYM_OR_REM) &&
else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_ASYM_MD) (lpa & PHY_X_P_SYM_MD))
skge->flow_control = FLOW_MODE_REM_SEND; skge->flow_status = FLOW_STAT_SYMMETRIC;
else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_BOTH_MD) else if (skge->flow_control == FLOW_MODE_SYM_OR_REM &&
skge->flow_control = FLOW_MODE_LOC_SEND; (lpa & PHY_X_RS_PAUSE) == PHY_X_P_ASYM_MD)
/* Enable PAUSE receive, disable PAUSE transmit */
skge->flow_status = FLOW_STAT_REM_SEND;
else if (skge->flow_control == FLOW_MODE_LOC_SEND &&
(lpa & PHY_X_RS_PAUSE) == PHY_X_P_BOTH_MD)
/* Disable PAUSE receive, enable PAUSE transmit */
skge->flow_status = FLOW_STAT_LOC_SEND;
else else
skge->flow_control = FLOW_MODE_NONE; skge->flow_status = FLOW_STAT_NONE;
skge->speed = SPEED_1000; skge->speed = SPEED_1000;
} }
@ -1602,8 +1626,8 @@ static void genesis_link_up(struct skge_port *skge)
* enabling pause frame reception is required for 1000BT * enabling pause frame reception is required for 1000BT
* because the XMAC is not reset if the link is going down * because the XMAC is not reset if the link is going down
*/ */
if (skge->flow_control == FLOW_MODE_NONE || if (skge->flow_status == FLOW_STAT_NONE ||
skge->flow_control == FLOW_MODE_LOC_SEND) skge->flow_status == FLOW_STAT_LOC_SEND)
/* Disable Pause Frame Reception */ /* Disable Pause Frame Reception */
cmd |= XM_MMU_IGN_PF; cmd |= XM_MMU_IGN_PF;
else else
@ -1613,8 +1637,8 @@ static void genesis_link_up(struct skge_port *skge)
xm_write16(hw, port, XM_MMU_CMD, cmd); xm_write16(hw, port, XM_MMU_CMD, cmd);
mode = xm_read32(hw, port, XM_MODE); mode = xm_read32(hw, port, XM_MODE);
if (skge->flow_control == FLOW_MODE_SYMMETRIC || if (skge->flow_status== FLOW_STAT_SYMMETRIC ||
skge->flow_control == FLOW_MODE_LOC_SEND) { skge->flow_status == FLOW_STAT_LOC_SEND) {
/* /*
* Configure Pause Frame Generation * Configure Pause Frame Generation
* Use internal and external Pause Frame Generation. * Use internal and external Pause Frame Generation.
@ -1938,6 +1962,11 @@ static void yukon_mac_init(struct skge_hw *hw, int port)
case FLOW_MODE_LOC_SEND: case FLOW_MODE_LOC_SEND:
/* disable Rx flow-control */ /* disable Rx flow-control */
reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS;
break;
case FLOW_MODE_SYMMETRIC:
case FLOW_MODE_SYM_OR_REM:
/* enable Tx & Rx flow-control */
break;
} }
gma_write16(hw, port, GM_GP_CTRL, reg); gma_write16(hw, port, GM_GP_CTRL, reg);
@ -2132,13 +2161,11 @@ static void yukon_link_down(struct skge_port *skge)
ctrl &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA); ctrl &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
gma_write16(hw, port, GM_GP_CTRL, ctrl); gma_write16(hw, port, GM_GP_CTRL, ctrl);
if (skge->flow_control == FLOW_MODE_REM_SEND) { if (skge->flow_status == FLOW_STAT_REM_SEND) {
ctrl = gm_phy_read(hw, port, PHY_MARV_AUNE_ADV);
ctrl |= PHY_M_AN_ASP;
/* restore Asymmetric Pause bit */ /* restore Asymmetric Pause bit */
gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, ctrl);
gm_phy_read(hw, port,
PHY_MARV_AUNE_ADV)
| PHY_M_AN_ASP);
} }
yukon_reset(hw, port); yukon_reset(hw, port);
@ -2185,19 +2212,19 @@ static void yukon_phy_intr(struct skge_port *skge)
/* We are using IEEE 802.3z/D5.0 Table 37-4 */ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
switch (phystat & PHY_M_PS_PAUSE_MSK) { switch (phystat & PHY_M_PS_PAUSE_MSK) {
case PHY_M_PS_PAUSE_MSK: case PHY_M_PS_PAUSE_MSK:
skge->flow_control = FLOW_MODE_SYMMETRIC; skge->flow_status = FLOW_STAT_SYMMETRIC;
break; break;
case PHY_M_PS_RX_P_EN: case PHY_M_PS_RX_P_EN:
skge->flow_control = FLOW_MODE_REM_SEND; skge->flow_status = FLOW_STAT_REM_SEND;
break; break;
case PHY_M_PS_TX_P_EN: case PHY_M_PS_TX_P_EN:
skge->flow_control = FLOW_MODE_LOC_SEND; skge->flow_status = FLOW_STAT_LOC_SEND;
break; break;
default: default:
skge->flow_control = FLOW_MODE_NONE; skge->flow_status = FLOW_STAT_NONE;
} }
if (skge->flow_control == FLOW_MODE_NONE || if (skge->flow_status == FLOW_STAT_NONE ||
(skge->speed < SPEED_1000 && skge->duplex == DUPLEX_HALF)) (skge->speed < SPEED_1000 && skge->duplex == DUPLEX_HALF))
skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
else else
@ -3420,7 +3447,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
/* Auto speed and flow control */ /* Auto speed and flow control */
skge->autoneg = AUTONEG_ENABLE; skge->autoneg = AUTONEG_ENABLE;
skge->flow_control = FLOW_MODE_SYMMETRIC; skge->flow_control = FLOW_MODE_SYM_OR_REM;
skge->duplex = -1; skge->duplex = -1;
skge->speed = -1; skge->speed = -1;
skge->advertising = skge_supported_modes(hw); skge->advertising = skge_supported_modes(hw);

View File

@ -2427,13 +2427,24 @@ struct skge_hw {
struct mutex phy_mutex; struct mutex phy_mutex;
}; };
enum { enum pause_control {
FLOW_MODE_NONE = 0, /* No Flow-Control */ FLOW_MODE_NONE = 1, /* No Flow-Control */
FLOW_MODE_LOC_SEND = 1, /* Local station sends PAUSE */ FLOW_MODE_LOC_SEND = 2, /* Local station sends PAUSE */
FLOW_MODE_REM_SEND = 2, /* Symmetric or just remote */
FLOW_MODE_SYMMETRIC = 3, /* Both stations may send PAUSE */ FLOW_MODE_SYMMETRIC = 3, /* Both stations may send PAUSE */
FLOW_MODE_SYM_OR_REM = 4, /* Both stations may send PAUSE or
* just the remote station may send PAUSE
*/
}; };
enum pause_status {
FLOW_STAT_INDETERMINATED=0, /* indeterminated */
FLOW_STAT_NONE, /* No Flow Control */
FLOW_STAT_REM_SEND, /* Remote Station sends PAUSE */
FLOW_STAT_LOC_SEND, /* Local station sends PAUSE */
FLOW_STAT_SYMMETRIC, /* Both station may send PAUSE */
};
struct skge_port { struct skge_port {
u32 msg_enable; u32 msg_enable;
struct skge_hw *hw; struct skge_hw *hw;
@ -2446,9 +2457,10 @@ struct skge_port {
struct net_device_stats net_stats; struct net_device_stats net_stats;
struct work_struct link_thread; struct work_struct link_thread;
enum pause_control flow_control;
enum pause_status flow_status;
u8 rx_csum; u8 rx_csum;
u8 blink_on; u8 blink_on;
u8 flow_control;
u8 wol; u8 wol;
u8 autoneg; /* AUTONEG_ENABLE, AUTONEG_DISABLE */ u8 autoneg; /* AUTONEG_ENABLE, AUTONEG_DISABLE */
u8 duplex; /* DUPLEX_HALF, DUPLEX_FULL */ u8 duplex; /* DUPLEX_HALF, DUPLEX_FULL */