amd-xgbe: Fix flow control setting logic
authorLendacky, Thomas <Thomas.Lendacky@amd.com>
Thu, 14 May 2015 16:44:27 +0000 (11:44 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 May 2015 19:21:43 +0000 (15:21 -0400)
The flow control negotiation logic is flawed and does not properly
advertise and process auto-negotiation of the flow control settings.
Update the flow control support to properly set the flow control
auto-negotiation settings and process the results approrpriately.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
drivers/net/ethernet/amd/xgbe/xgbe.h

index 401703fc7b4f73d1c78a087fcae33f3fedfa8304..e4521799ba9cf012cd7446b62dc6f5e4db603dac 100644 (file)
@@ -782,8 +782,6 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
 {
        pdata->phy_link = -1;
        pdata->phy_speed = SPEED_UNKNOWN;
-       pdata->phy_tx_pause = pdata->tx_pause;
-       pdata->phy_rx_pause = pdata->rx_pause;
 
        return pdata->phy_if.phy_reset(pdata);
 }
index b24a78c3980095c3220f86d19754544beabc84a7..59e090e95c0e8648cfc27cac1fc63981c102df11 100644 (file)
@@ -247,9 +247,9 @@ static void xgbe_get_pauseparam(struct net_device *netdev,
 
        DBGPR("-->xgbe_get_pauseparam\n");
 
-       pause->autoneg = pdata->pause_autoneg;
-       pause->tx_pause = pdata->tx_pause;
-       pause->rx_pause = pdata->rx_pause;
+       pause->autoneg = pdata->phy.pause_autoneg;
+       pause->tx_pause = pdata->phy.tx_pause;
+       pause->rx_pause = pdata->phy.rx_pause;
 
        DBGPR("<--xgbe_get_pauseparam\n");
 }
@@ -265,19 +265,24 @@ static int xgbe_set_pauseparam(struct net_device *netdev,
        DBGPR("  autoneg = %d, tx_pause = %d, rx_pause = %d\n",
              pause->autoneg, pause->tx_pause, pause->rx_pause);
 
-       pdata->pause_autoneg = pause->autoneg;
-       if (pause->autoneg) {
-               pdata->phy.advertising |= ADVERTISED_Pause;
-               pdata->phy.advertising |= ADVERTISED_Asym_Pause;
+       if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE))
+               return -EINVAL;
+
+       pdata->phy.pause_autoneg = pause->autoneg;
+       pdata->phy.tx_pause = pause->tx_pause;
+       pdata->phy.rx_pause = pause->rx_pause;
 
-       } else {
-               pdata->phy.advertising &= ~ADVERTISED_Pause;
-               pdata->phy.advertising &= ~ADVERTISED_Asym_Pause;
+       pdata->phy.advertising &= ~ADVERTISED_Pause;
+       pdata->phy.advertising &= ~ADVERTISED_Asym_Pause;
 
-               pdata->tx_pause = pause->tx_pause;
-               pdata->rx_pause = pause->rx_pause;
+       if (pause->rx_pause) {
+               pdata->phy.advertising |= ADVERTISED_Pause;
+               pdata->phy.advertising |= ADVERTISED_Asym_Pause;
        }
 
+       if (pause->tx_pause)
+               pdata->phy.advertising ^= ADVERTISED_Asym_Pause;
+
        if (netif_running(netdev))
                ret = pdata->phy_if.phy_config_aneg(pdata);
 
index 1ae4bfbd13d3024ae6bb36cbb38ca29ac24312f1..cea19a37806ef7000cd4edfcff958c502f607703 100644 (file)
@@ -737,6 +737,18 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata)
        XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg);
 }
 
+static const char *xgbe_phy_fc_string(struct xgbe_prv_data *pdata)
+{
+       if (pdata->tx_pause && pdata->rx_pause)
+               return "rx/tx";
+       else if (pdata->rx_pause)
+               return "rx";
+       else if (pdata->tx_pause)
+               return "tx";
+       else
+               return "off";
+}
+
 static const char *xgbe_phy_speed_string(int speed)
 {
        switch (speed) {
@@ -760,7 +772,7 @@ static void xgbe_phy_print_status(struct xgbe_prv_data *pdata)
                            "Link is Up - %s/%s - flow control %s\n",
                            xgbe_phy_speed_string(pdata->phy.speed),
                            pdata->phy.duplex == DUPLEX_FULL ? "Full" : "Half",
-                           pdata->phy.pause ? "rx/tx" : "off");
+                           xgbe_phy_fc_string(pdata));
        else
                netdev_info(pdata->netdev, "Link is Down\n");
 }
@@ -771,24 +783,18 @@ static void xgbe_phy_adjust_link(struct xgbe_prv_data *pdata)
 
        if (pdata->phy.link) {
                /* Flow control support */
-               if (pdata->pause_autoneg) {
-                       if (pdata->phy.pause || pdata->phy.asym_pause) {
-                               pdata->tx_pause = 1;
-                               pdata->rx_pause = 1;
-                       } else {
-                               pdata->tx_pause = 0;
-                               pdata->rx_pause = 0;
-                       }
-               }
+               pdata->pause_autoneg = pdata->phy.pause_autoneg;
 
-               if (pdata->tx_pause != pdata->phy_tx_pause) {
+               if (pdata->tx_pause != pdata->phy.tx_pause) {
+                       new_state = 1;
                        pdata->hw_if.config_tx_flow_control(pdata);
-                       pdata->phy_tx_pause = pdata->tx_pause;
+                       pdata->tx_pause = pdata->phy.tx_pause;
                }
 
-               if (pdata->rx_pause != pdata->phy_rx_pause) {
+               if (pdata->rx_pause != pdata->phy.rx_pause) {
+                       new_state = 1;
                        pdata->hw_if.config_rx_flow_control(pdata);
-                       pdata->phy_rx_pause = pdata->rx_pause;
+                       pdata->rx_pause = pdata->phy.rx_pause;
                }
 
                /* Speed support */
@@ -835,9 +841,6 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
        if (pdata->phy.duplex != DUPLEX_FULL)
                return -EINVAL;
 
-       pdata->phy.pause = 0;
-       pdata->phy.asym_pause = 0;
-
        return 0;
 }
 
@@ -933,8 +936,6 @@ static void xgbe_phy_status_force(struct xgbe_prv_data *pdata)
                }
        }
        pdata->phy.duplex = DUPLEX_FULL;
-       pdata->phy.pause = 0;
-       pdata->phy.asym_pause = 0;
 }
 
 static void xgbe_phy_status_aneg(struct xgbe_prv_data *pdata)
@@ -957,9 +958,21 @@ static void xgbe_phy_status_aneg(struct xgbe_prv_data *pdata)
        if (lp_reg & 0x800)
                pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
 
-       ad_reg &= lp_reg;
-       pdata->phy.pause = (ad_reg & 0x400) ? 1 : 0;
-       pdata->phy.asym_pause = (ad_reg & 0x800) ? 1 : 0;
+       if (pdata->phy.pause_autoneg) {
+               /* Set flow control based on auto-negotiation result */
+               pdata->phy.tx_pause = 0;
+               pdata->phy.rx_pause = 0;
+
+               if (ad_reg & lp_reg & 0x400) {
+                       pdata->phy.tx_pause = 1;
+                       pdata->phy.rx_pause = 1;
+               } else if (ad_reg & lp_reg & 0x800) {
+                       if (ad_reg & 0x400)
+                               pdata->phy.rx_pause = 1;
+                       else if (lp_reg & 0x400)
+                               pdata->phy.tx_pause = 1;
+               }
+       }
 
        /* Compare Advertisement and Link Partner register 2 */
        ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
@@ -1223,6 +1236,22 @@ static void xgbe_phy_init(struct xgbe_prv_data *pdata)
 
        pdata->phy.link = 0;
 
+       pdata->phy.pause_autoneg = pdata->pause_autoneg;
+       pdata->phy.tx_pause = pdata->tx_pause;
+       pdata->phy.rx_pause = pdata->rx_pause;
+
+       /* Fix up Flow Control advertising */
+       pdata->phy.advertising &= ~ADVERTISED_Pause;
+       pdata->phy.advertising &= ~ADVERTISED_Asym_Pause;
+
+       if (pdata->rx_pause) {
+               pdata->phy.advertising |= ADVERTISED_Pause;
+               pdata->phy.advertising |= ADVERTISED_Asym_Pause;
+       }
+
+       if (pdata->tx_pause)
+               pdata->phy.advertising ^= ADVERTISED_Asym_Pause;
+
        if (netif_msg_drv(pdata))
                xgbe_dump_phy_registers(pdata);
 }
index f535d19da803b20c2aa07cf46a8c02f1988b2ce7..63d72a140053956c35b82b2982497db2fbf1e440 100644 (file)
@@ -539,10 +539,12 @@ struct xgbe_phy {
        int autoneg;
        int speed;
        int duplex;
-       int pause;
-       int asym_pause;
 
        int link;
+
+       int pause_autoneg;
+       int tx_pause;
+       int rx_pause;
 };
 
 struct xgbe_mmc_stats {
@@ -910,8 +912,6 @@ struct xgbe_prv_data {
        phy_interface_t phy_mode;
        int phy_link;
        int phy_speed;
-       unsigned int phy_tx_pause;
-       unsigned int phy_rx_pause;
 
        /* MDIO/PHY related settings */
        struct xgbe_phy phy;