diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-10-17 13:24:13 -0400 |
---|---|---|
committer | Stephen Hemminger <shemminger@osdl.org> | 2006-10-17 13:24:13 -0400 |
commit | 16ad91e1c686734aaa5664cd08af0b5e9bf3af61 (patch) | |
tree | 2e4c5d3e4932c66d2c00eddee81b2df0667970fe /drivers/net/sky2.c | |
parent | 7800fddcd05a7dc89276389b96664af4f7890ea7 (diff) |
sky2: flow control setting fixes
The result of flow control negotiation should not limit the next
negotiatition. If board is plugged into an old half duplex 10Mbit port,
without pause, then replugged into a gigabit port, it should negotiate
what is desired, not inherit that last negotiation.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r-- | drivers/net/sky2.c | 115 |
1 files changed, 72 insertions, 43 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 20a8c34e6d53..b8f202169a75 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -284,6 +284,31 @@ static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port) | |||
284 | gma_write16(hw, port, GM_RX_CTRL, reg); | 284 | gma_write16(hw, port, GM_RX_CTRL, reg); |
285 | } | 285 | } |
286 | 286 | ||
287 | /* flow control to advertise bits */ | ||
288 | static const u16 copper_fc_adv[] = { | ||
289 | [FC_NONE] = 0, | ||
290 | [FC_TX] = PHY_M_AN_ASP, | ||
291 | [FC_RX] = PHY_M_AN_PC, | ||
292 | [FC_BOTH] = PHY_M_AN_PC | PHY_M_AN_ASP, | ||
293 | }; | ||
294 | |||
295 | /* flow control to advertise bits when using 1000BaseX */ | ||
296 | static const u16 fiber_fc_adv[] = { | ||
297 | [FC_BOTH] = PHY_M_P_BOTH_MD_X, | ||
298 | [FC_TX] = PHY_M_P_ASYM_MD_X, | ||
299 | [FC_RX] = PHY_M_P_SYM_MD_X, | ||
300 | [FC_NONE] = PHY_M_P_NO_PAUSE_X, | ||
301 | }; | ||
302 | |||
303 | /* flow control to GMA disable bits */ | ||
304 | static const u16 gm_fc_disable[] = { | ||
305 | [FC_NONE] = GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS, | ||
306 | [FC_TX] = GM_GPCR_FC_RX_DIS, | ||
307 | [FC_RX] = GM_GPCR_FC_TX_DIS, | ||
308 | [FC_BOTH] = 0, | ||
309 | }; | ||
310 | |||
311 | |||
287 | static void sky2_phy_init(struct sky2_hw *hw, unsigned port) | 312 | static void sky2_phy_init(struct sky2_hw *hw, unsigned port) |
288 | { | 313 | { |
289 | struct sky2_port *sky2 = netdev_priv(hw->dev[port]); | 314 | struct sky2_port *sky2 = netdev_priv(hw->dev[port]); |
@@ -376,29 +401,14 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) | |||
376 | if (sky2->advertising & ADVERTISED_10baseT_Half) | 401 | if (sky2->advertising & ADVERTISED_10baseT_Half) |
377 | adv |= PHY_M_AN_10_HD; | 402 | adv |= PHY_M_AN_10_HD; |
378 | 403 | ||
379 | /* desired flow control */ | 404 | adv |= copper_fc_adv[sky2->flow_mode]; |
380 | if (sky2->tx_pause && sky2->rx_pause) /* both */ | ||
381 | adv |= PHY_M_AN_PC | PHY_M_AN_ASP; | ||
382 | else if (sky2->tx_pause) | ||
383 | adv |= PHY_M_AN_ASP; | ||
384 | else if (sky2->rx_pause) | ||
385 | adv |= PHY_M_AN_PC; | ||
386 | |||
387 | |||
388 | } else { /* special defines for FIBER (88E1040S only) */ | 405 | } else { /* special defines for FIBER (88E1040S only) */ |
389 | if (sky2->advertising & ADVERTISED_1000baseT_Full) | 406 | if (sky2->advertising & ADVERTISED_1000baseT_Full) |
390 | adv |= PHY_M_AN_1000X_AFD; | 407 | adv |= PHY_M_AN_1000X_AFD; |
391 | if (sky2->advertising & ADVERTISED_1000baseT_Half) | 408 | if (sky2->advertising & ADVERTISED_1000baseT_Half) |
392 | adv |= PHY_M_AN_1000X_AHD; | 409 | adv |= PHY_M_AN_1000X_AHD; |
393 | 410 | ||
394 | if (sky2->tx_pause && sky2->rx_pause) /* both */ | 411 | adv |= fiber_fc_adv[sky2->flow_mode]; |
395 | adv |= PHY_M_P_BOTH_MD_X; | ||
396 | else if (sky2->tx_pause) | ||
397 | adv |= PHY_M_P_ASYM_MD_X; | ||
398 | else if (sky2->rx_pause) | ||
399 | adv |= PHY_M_P_SYM_MD_X; | ||
400 | else | ||
401 | adv |= PHY_M_P_NO_PAUSE_X; | ||
402 | } | 412 | } |
403 | 413 | ||
404 | /* Restart Auto-negotiation */ | 414 | /* Restart Auto-negotiation */ |
@@ -424,20 +434,14 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) | |||
424 | if (sky2->duplex == DUPLEX_FULL) { | 434 | if (sky2->duplex == DUPLEX_FULL) { |
425 | reg |= GM_GPCR_DUP_FULL; | 435 | reg |= GM_GPCR_DUP_FULL; |
426 | ctrl |= PHY_CT_DUP_MD; | 436 | ctrl |= PHY_CT_DUP_MD; |
427 | } else if (sky2->speed != SPEED_1000 && hw->chip_id != CHIP_ID_YUKON_EC_U) { | 437 | } else if (sky2->speed < SPEED_1000) |
428 | /* Turn off flow control for 10/100mbps */ | 438 | sky2->flow_mode = FC_NONE; |
429 | sky2->rx_pause = 0; | ||
430 | sky2->tx_pause = 0; | ||
431 | } | ||
432 | 439 | ||
433 | if (!sky2->rx_pause) | ||
434 | reg |= GM_GPCR_FC_RX_DIS; | ||
435 | 440 | ||
436 | if (!sky2->tx_pause) | 441 | reg |= gm_fc_disable[sky2->flow_mode]; |
437 | reg |= GM_GPCR_FC_TX_DIS; | ||
438 | 442 | ||
439 | /* Forward pause packets to GMAC? */ | 443 | /* Forward pause packets to GMAC? */ |
440 | if (sky2->tx_pause || sky2->rx_pause) | 444 | if (sky2->flow_mode & FC_RX) |
441 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); | 445 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); |
442 | else | 446 | else |
443 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); | 447 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); |
@@ -1605,6 +1609,12 @@ static void sky2_link_up(struct sky2_port *sky2) | |||
1605 | struct sky2_hw *hw = sky2->hw; | 1609 | struct sky2_hw *hw = sky2->hw; |
1606 | unsigned port = sky2->port; | 1610 | unsigned port = sky2->port; |
1607 | u16 reg; | 1611 | u16 reg; |
1612 | static const char *fc_name[] = { | ||
1613 | [FC_NONE] = "none", | ||
1614 | [FC_TX] = "tx", | ||
1615 | [FC_RX] = "rx", | ||
1616 | [FC_BOTH] = "both", | ||
1617 | }; | ||
1608 | 1618 | ||
1609 | /* enable Rx/Tx */ | 1619 | /* enable Rx/Tx */ |
1610 | reg = gma_read16(hw, port, GM_GP_CTRL); | 1620 | reg = gma_read16(hw, port, GM_GP_CTRL); |
@@ -1648,8 +1658,7 @@ static void sky2_link_up(struct sky2_port *sky2) | |||
1648 | "%s: Link is up at %d Mbps, %s duplex, flow control %s\n", | 1658 | "%s: Link is up at %d Mbps, %s duplex, flow control %s\n", |
1649 | sky2->netdev->name, sky2->speed, | 1659 | sky2->netdev->name, sky2->speed, |
1650 | sky2->duplex == DUPLEX_FULL ? "full" : "half", | 1660 | sky2->duplex == DUPLEX_FULL ? "full" : "half", |
1651 | (sky2->tx_pause && sky2->rx_pause) ? "both" : | 1661 | fc_name[sky2->flow_status]); |
1652 | sky2->tx_pause ? "tx" : sky2->rx_pause ? "rx" : "none"); | ||
1653 | } | 1662 | } |
1654 | 1663 | ||
1655 | static void sky2_link_down(struct sky2_port *sky2) | 1664 | static void sky2_link_down(struct sky2_port *sky2) |
@@ -1664,7 +1673,7 @@ static void sky2_link_down(struct sky2_port *sky2) | |||
1664 | reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA); | 1673 | reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA); |
1665 | gma_write16(hw, port, GM_GP_CTRL, reg); | 1674 | gma_write16(hw, port, GM_GP_CTRL, reg); |
1666 | 1675 | ||
1667 | if (sky2->rx_pause && !sky2->tx_pause) { | 1676 | if (sky2->flow_status == FC_RX) { |
1668 | /* restore Asymmetric Pause bit */ | 1677 | /* restore Asymmetric Pause bit */ |
1669 | gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, | 1678 | gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, |
1670 | gm_phy_read(hw, port, PHY_MARV_AUNE_ADV) | 1679 | gm_phy_read(hw, port, PHY_MARV_AUNE_ADV) |
@@ -1683,6 +1692,14 @@ static void sky2_link_down(struct sky2_port *sky2) | |||
1683 | sky2_phy_init(hw, port); | 1692 | sky2_phy_init(hw, port); |
1684 | } | 1693 | } |
1685 | 1694 | ||
1695 | static enum flow_control sky2_flow(int rx, int tx) | ||
1696 | { | ||
1697 | if (rx) | ||
1698 | return tx ? FC_BOTH : FC_RX; | ||
1699 | else | ||
1700 | return tx ? FC_TX : FC_NONE; | ||
1701 | } | ||
1702 | |||
1686 | static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) | 1703 | static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) |
1687 | { | 1704 | { |
1688 | struct sky2_hw *hw = sky2->hw; | 1705 | struct sky2_hw *hw = sky2->hw; |
@@ -1709,14 +1726,14 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) | |||
1709 | if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) | 1726 | if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) |
1710 | aux >>= 6; | 1727 | aux >>= 6; |
1711 | 1728 | ||
1712 | sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0; | 1729 | sky2->flow_status = sky2_flow(aux & PHY_M_PS_RX_P_EN, |
1713 | sky2->tx_pause = (aux & PHY_M_PS_TX_P_EN) != 0; | 1730 | aux & PHY_M_PS_TX_P_EN); |
1714 | 1731 | ||
1715 | if (sky2->duplex == DUPLEX_HALF && sky2->speed != SPEED_1000 | 1732 | if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 |
1716 | && hw->chip_id != CHIP_ID_YUKON_EC_U) | 1733 | && hw->chip_id != CHIP_ID_YUKON_EC_U) |
1717 | sky2->rx_pause = sky2->tx_pause = 0; | 1734 | sky2->flow_status = FC_NONE; |
1718 | 1735 | ||
1719 | if (sky2->rx_pause || sky2->tx_pause) | 1736 | if (aux & PHY_M_PS_RX_P_EN) |
1720 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); | 1737 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); |
1721 | else | 1738 | else |
1722 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); | 1739 | sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); |
@@ -2729,7 +2746,7 @@ static int sky2_nway_reset(struct net_device *dev) | |||
2729 | { | 2746 | { |
2730 | struct sky2_port *sky2 = netdev_priv(dev); | 2747 | struct sky2_port *sky2 = netdev_priv(dev); |
2731 | 2748 | ||
2732 | if (sky2->autoneg != AUTONEG_ENABLE) | 2749 | if (!netif_running(dev) || sky2->autoneg != AUTONEG_ENABLE) |
2733 | return -EINVAL; | 2750 | return -EINVAL; |
2734 | 2751 | ||
2735 | sky2_phy_reinit(sky2); | 2752 | sky2_phy_reinit(sky2); |
@@ -2971,8 +2988,20 @@ static void sky2_get_pauseparam(struct net_device *dev, | |||
2971 | { | 2988 | { |
2972 | struct sky2_port *sky2 = netdev_priv(dev); | 2989 | struct sky2_port *sky2 = netdev_priv(dev); |
2973 | 2990 | ||
2974 | ecmd->tx_pause = sky2->tx_pause; | 2991 | switch (sky2->flow_mode) { |
2975 | ecmd->rx_pause = sky2->rx_pause; | 2992 | case FC_NONE: |
2993 | ecmd->tx_pause = ecmd->rx_pause = 0; | ||
2994 | break; | ||
2995 | case FC_TX: | ||
2996 | ecmd->tx_pause = 1, ecmd->rx_pause = 0; | ||
2997 | break; | ||
2998 | case FC_RX: | ||
2999 | ecmd->tx_pause = 0, ecmd->rx_pause = 1; | ||
3000 | break; | ||
3001 | case FC_BOTH: | ||
3002 | ecmd->tx_pause = ecmd->rx_pause = 1; | ||
3003 | } | ||
3004 | |||
2976 | ecmd->autoneg = sky2->autoneg; | 3005 | ecmd->autoneg = sky2->autoneg; |
2977 | } | 3006 | } |
2978 | 3007 | ||
@@ -2982,10 +3011,10 @@ static int sky2_set_pauseparam(struct net_device *dev, | |||
2982 | struct sky2_port *sky2 = netdev_priv(dev); | 3011 | struct sky2_port *sky2 = netdev_priv(dev); |
2983 | 3012 | ||
2984 | sky2->autoneg = ecmd->autoneg; | 3013 | sky2->autoneg = ecmd->autoneg; |
2985 | sky2->tx_pause = ecmd->tx_pause != 0; | 3014 | sky2->flow_mode = sky2_flow(ecmd->rx_pause, ecmd->tx_pause); |
2986 | sky2->rx_pause = ecmd->rx_pause != 0; | ||
2987 | 3015 | ||
2988 | sky2_phy_reinit(sky2); | 3016 | if (netif_running(dev)) |
3017 | sky2_phy_reinit(sky2); | ||
2989 | 3018 | ||
2990 | return 0; | 3019 | return 0; |
2991 | } | 3020 | } |
@@ -3215,8 +3244,8 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, | |||
3215 | 3244 | ||
3216 | /* Auto speed and flow control */ | 3245 | /* Auto speed and flow control */ |
3217 | sky2->autoneg = AUTONEG_ENABLE; | 3246 | sky2->autoneg = AUTONEG_ENABLE; |
3218 | sky2->tx_pause = 1; | 3247 | sky2->flow_mode = FC_BOTH; |
3219 | sky2->rx_pause = 1; | 3248 | |
3220 | sky2->duplex = -1; | 3249 | sky2->duplex = -1; |
3221 | sky2->speed = -1; | 3250 | sky2->speed = -1; |
3222 | sky2->advertising = sky2_supported_modes(hw); | 3251 | sky2->advertising = sky2_supported_modes(hw); |