diff options
author | Claudiu Manoil <claudiu.manoil@freescale.com> | 2013-08-12 06:53:26 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-08-13 18:28:53 -0400 |
commit | 23402bddf9e56eecb27bbd1e5467b3b79b3dbe58 (patch) | |
tree | 26dc3784f50481c3ac8457abccd54bd7aec1748a /drivers/net/ethernet/freescale | |
parent | ebd8b934e23f45ad3fc8a5a28bc5a96741a6a106 (diff) |
gianfar: Add flow control support
eTSEC has Rx and Tx flow control capabilities that may be enabled
through MACCFG1[Rx_Flow, Tx_Flow] bits. These bits must not be set
however when eTSEC is operated in Half-Duplex mode. Unfortunately,
the driver currently sets these bits unconditionally.
This patch adds the proper handling of the PAUSE frame capability
register bits by implementing the ethtool -A interface. When pause
autoneg is enabled, the controller uses the phy's capability to
negotiate PAUSE frame settings with the link partner and reconfigures
its Rx_Flow and Tx_Flow settings to match the capabilities of the
link partner. If pause autoneg is off, the PAUSE frame generation
may be forced manually (ethtool -A). Flow control is disabled by
default now.
This implementation is inspired by the tg3 driver.
Signed-off-by: Lutz Jaenicke <ljaenicke@innominate.com>
Signed-off-by: Claudiu Manoil <claudiu.manoil@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale')
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar.c | 51 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar.h | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar_ethtool.c | 74 |
3 files changed, 132 insertions, 3 deletions
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 3cb464780777..b2c91dcd245f 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c | |||
@@ -1016,7 +1016,14 @@ static int gfar_probe(struct platform_device *ofdev) | |||
1016 | /* We need to delay at least 3 TX clocks */ | 1016 | /* We need to delay at least 3 TX clocks */ |
1017 | udelay(2); | 1017 | udelay(2); |
1018 | 1018 | ||
1019 | tempval = (MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); | 1019 | tempval = 0; |
1020 | if (!priv->pause_aneg_en && priv->tx_pause_en) | ||
1021 | tempval |= MACCFG1_TX_FLOW; | ||
1022 | if (!priv->pause_aneg_en && priv->rx_pause_en) | ||
1023 | tempval |= MACCFG1_RX_FLOW; | ||
1024 | /* the soft reset bit is not self-resetting, so we need to | ||
1025 | * clear it before resuming normal operation | ||
1026 | */ | ||
1020 | gfar_write(®s->maccfg1, tempval); | 1027 | gfar_write(®s->maccfg1, tempval); |
1021 | 1028 | ||
1022 | /* Initialize MACCFG2. */ | 1029 | /* Initialize MACCFG2. */ |
@@ -1460,7 +1467,7 @@ static int init_phy(struct net_device *dev) | |||
1460 | struct gfar_private *priv = netdev_priv(dev); | 1467 | struct gfar_private *priv = netdev_priv(dev); |
1461 | uint gigabit_support = | 1468 | uint gigabit_support = |
1462 | priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ? | 1469 | priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ? |
1463 | SUPPORTED_1000baseT_Full : 0; | 1470 | GFAR_SUPPORTED_GBIT : 0; |
1464 | phy_interface_t interface; | 1471 | phy_interface_t interface; |
1465 | 1472 | ||
1466 | priv->oldlink = 0; | 1473 | priv->oldlink = 0; |
@@ -3023,6 +3030,41 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id) | |||
3023 | return IRQ_HANDLED; | 3030 | return IRQ_HANDLED; |
3024 | } | 3031 | } |
3025 | 3032 | ||
3033 | static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv) | ||
3034 | { | ||
3035 | struct phy_device *phydev = priv->phydev; | ||
3036 | u32 val = 0; | ||
3037 | |||
3038 | if (!phydev->duplex) | ||
3039 | return val; | ||
3040 | |||
3041 | if (!priv->pause_aneg_en) { | ||
3042 | if (priv->tx_pause_en) | ||
3043 | val |= MACCFG1_TX_FLOW; | ||
3044 | if (priv->rx_pause_en) | ||
3045 | val |= MACCFG1_RX_FLOW; | ||
3046 | } else { | ||
3047 | u16 lcl_adv, rmt_adv; | ||
3048 | u8 flowctrl; | ||
3049 | /* get link partner capabilities */ | ||
3050 | rmt_adv = 0; | ||
3051 | if (phydev->pause) | ||
3052 | rmt_adv = LPA_PAUSE_CAP; | ||
3053 | if (phydev->asym_pause) | ||
3054 | rmt_adv |= LPA_PAUSE_ASYM; | ||
3055 | |||
3056 | lcl_adv = mii_advertise_flowctrl(phydev->advertising); | ||
3057 | |||
3058 | flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); | ||
3059 | if (flowctrl & FLOW_CTRL_TX) | ||
3060 | val |= MACCFG1_TX_FLOW; | ||
3061 | if (flowctrl & FLOW_CTRL_RX) | ||
3062 | val |= MACCFG1_RX_FLOW; | ||
3063 | } | ||
3064 | |||
3065 | return val; | ||
3066 | } | ||
3067 | |||
3026 | /* Called every time the controller might need to be made | 3068 | /* Called every time the controller might need to be made |
3027 | * aware of new link state. The PHY code conveys this | 3069 | * aware of new link state. The PHY code conveys this |
3028 | * information through variables in the phydev structure, and this | 3070 | * information through variables in the phydev structure, and this |
@@ -3041,6 +3083,7 @@ static void adjust_link(struct net_device *dev) | |||
3041 | lock_tx_qs(priv); | 3083 | lock_tx_qs(priv); |
3042 | 3084 | ||
3043 | if (phydev->link) { | 3085 | if (phydev->link) { |
3086 | u32 tempval1 = gfar_read(®s->maccfg1); | ||
3044 | u32 tempval = gfar_read(®s->maccfg2); | 3087 | u32 tempval = gfar_read(®s->maccfg2); |
3045 | u32 ecntrl = gfar_read(®s->ecntrl); | 3088 | u32 ecntrl = gfar_read(®s->ecntrl); |
3046 | 3089 | ||
@@ -3089,6 +3132,10 @@ static void adjust_link(struct net_device *dev) | |||
3089 | priv->oldspeed = phydev->speed; | 3132 | priv->oldspeed = phydev->speed; |
3090 | } | 3133 | } |
3091 | 3134 | ||
3135 | tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); | ||
3136 | tempval1 |= gfar_get_flowctrl_cfg(priv); | ||
3137 | |||
3138 | gfar_write(®s->maccfg1, tempval1); | ||
3092 | gfar_write(®s->maccfg2, tempval); | 3139 | gfar_write(®s->maccfg2, tempval); |
3093 | gfar_write(®s->ecntrl, ecntrl); | 3140 | gfar_write(®s->ecntrl, ecntrl); |
3094 | 3141 | ||
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index ee19f2c138a6..46f56f36118f 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h | |||
@@ -146,6 +146,10 @@ extern const char gfar_driver_version[]; | |||
146 | | SUPPORTED_Autoneg \ | 146 | | SUPPORTED_Autoneg \ |
147 | | SUPPORTED_MII) | 147 | | SUPPORTED_MII) |
148 | 148 | ||
149 | #define GFAR_SUPPORTED_GBIT (SUPPORTED_1000baseT_Full \ | ||
150 | | SUPPORTED_Pause \ | ||
151 | | SUPPORTED_Asym_Pause) | ||
152 | |||
149 | /* TBI register addresses */ | 153 | /* TBI register addresses */ |
150 | #define MII_TBICON 0x11 | 154 | #define MII_TBICON 0x11 |
151 | 155 | ||
@@ -1100,7 +1104,11 @@ struct gfar_private { | |||
1100 | /* Wake-on-LAN enabled */ | 1104 | /* Wake-on-LAN enabled */ |
1101 | wol_en:1, | 1105 | wol_en:1, |
1102 | /* Enable priorty based Tx scheduling in Hw */ | 1106 | /* Enable priorty based Tx scheduling in Hw */ |
1103 | prio_sched_en:1; | 1107 | prio_sched_en:1, |
1108 | /* Flow control flags */ | ||
1109 | pause_aneg_en:1, | ||
1110 | tx_pause_en:1, | ||
1111 | rx_pause_en:1; | ||
1104 | 1112 | ||
1105 | /* The total tx and rx ring size for the enabled queues */ | 1113 | /* The total tx and rx ring size for the enabled queues */ |
1106 | unsigned int total_tx_ring_size; | 1114 | unsigned int total_tx_ring_size; |
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 21cd88124ca9..d3d7ede27ef1 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c | |||
@@ -535,6 +535,78 @@ static int gfar_sringparam(struct net_device *dev, | |||
535 | return err; | 535 | return err; |
536 | } | 536 | } |
537 | 537 | ||
538 | static void gfar_gpauseparam(struct net_device *dev, | ||
539 | struct ethtool_pauseparam *epause) | ||
540 | { | ||
541 | struct gfar_private *priv = netdev_priv(dev); | ||
542 | |||
543 | epause->autoneg = !!priv->pause_aneg_en; | ||
544 | epause->rx_pause = !!priv->rx_pause_en; | ||
545 | epause->tx_pause = !!priv->tx_pause_en; | ||
546 | } | ||
547 | |||
548 | static int gfar_spauseparam(struct net_device *dev, | ||
549 | struct ethtool_pauseparam *epause) | ||
550 | { | ||
551 | struct gfar_private *priv = netdev_priv(dev); | ||
552 | struct phy_device *phydev = priv->phydev; | ||
553 | struct gfar __iomem *regs = priv->gfargrp[0].regs; | ||
554 | u32 oldadv, newadv; | ||
555 | |||
556 | if (!(phydev->supported & SUPPORTED_Pause) || | ||
557 | (!(phydev->supported & SUPPORTED_Asym_Pause) && | ||
558 | (epause->rx_pause != epause->tx_pause))) | ||
559 | return -EINVAL; | ||
560 | |||
561 | priv->rx_pause_en = priv->tx_pause_en = 0; | ||
562 | if (epause->rx_pause) { | ||
563 | priv->rx_pause_en = 1; | ||
564 | |||
565 | if (epause->tx_pause) { | ||
566 | priv->tx_pause_en = 1; | ||
567 | /* FLOW_CTRL_RX & TX */ | ||
568 | newadv = ADVERTISED_Pause; | ||
569 | } else /* FLOW_CTLR_RX */ | ||
570 | newadv = ADVERTISED_Pause | ADVERTISED_Asym_Pause; | ||
571 | } else if (epause->tx_pause) { | ||
572 | priv->tx_pause_en = 1; | ||
573 | /* FLOW_CTLR_TX */ | ||
574 | newadv = ADVERTISED_Asym_Pause; | ||
575 | } else | ||
576 | newadv = 0; | ||
577 | |||
578 | if (epause->autoneg) | ||
579 | priv->pause_aneg_en = 1; | ||
580 | else | ||
581 | priv->pause_aneg_en = 0; | ||
582 | |||
583 | oldadv = phydev->advertising & | ||
584 | (ADVERTISED_Pause | ADVERTISED_Asym_Pause); | ||
585 | if (oldadv != newadv) { | ||
586 | phydev->advertising &= | ||
587 | ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); | ||
588 | phydev->advertising |= newadv; | ||
589 | if (phydev->autoneg) | ||
590 | /* inform link partner of our | ||
591 | * new flow ctrl settings | ||
592 | */ | ||
593 | return phy_start_aneg(phydev); | ||
594 | |||
595 | if (!epause->autoneg) { | ||
596 | u32 tempval; | ||
597 | tempval = gfar_read(®s->maccfg1); | ||
598 | tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); | ||
599 | if (priv->tx_pause_en) | ||
600 | tempval |= MACCFG1_TX_FLOW; | ||
601 | if (priv->rx_pause_en) | ||
602 | tempval |= MACCFG1_RX_FLOW; | ||
603 | gfar_write(®s->maccfg1, tempval); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | |||
538 | int gfar_set_features(struct net_device *dev, netdev_features_t features) | 610 | int gfar_set_features(struct net_device *dev, netdev_features_t features) |
539 | { | 611 | { |
540 | struct gfar_private *priv = netdev_priv(dev); | 612 | struct gfar_private *priv = netdev_priv(dev); |
@@ -1806,6 +1878,8 @@ const struct ethtool_ops gfar_ethtool_ops = { | |||
1806 | .set_coalesce = gfar_scoalesce, | 1878 | .set_coalesce = gfar_scoalesce, |
1807 | .get_ringparam = gfar_gringparam, | 1879 | .get_ringparam = gfar_gringparam, |
1808 | .set_ringparam = gfar_sringparam, | 1880 | .set_ringparam = gfar_sringparam, |
1881 | .get_pauseparam = gfar_gpauseparam, | ||
1882 | .set_pauseparam = gfar_spauseparam, | ||
1809 | .get_strings = gfar_gstrings, | 1883 | .get_strings = gfar_gstrings, |
1810 | .get_sset_count = gfar_sset_count, | 1884 | .get_sset_count = gfar_sset_count, |
1811 | .get_ethtool_stats = gfar_fill_stats, | 1885 | .get_ethtool_stats = gfar_fill_stats, |