ethtool can be used to enable/disable pause. Add a helper to configure
the PHY when asym pause is supported.

Signed-off-by: Andrew Lunn <and...@lunn.ch>
Reviewed-by: Florian Fainelli <f.faine...@gmail.com>
---
v3: Fix return from phy_set_asym_pause found by 0-day.
v2: Also trigger autoneg if the advertising settings have changed.
---
 .../ethernet/apm/xgene/xgene_enet_ethtool.c   | 26 ++--------
 drivers/net/ethernet/aurora/nb8800.c          |  9 +---
 drivers/net/ethernet/broadcom/tg3.c           | 43 +++++-----------
 drivers/net/ethernet/faraday/ftgmac100.c      | 17 ++-----
 .../ethernet/freescale/dpaa/dpaa_ethtool.c    | 23 +--------
 .../net/ethernet/freescale/gianfar_ethtool.c  | 49 ++++++-------------
 .../hisilicon/hns3/hns3pf/hclge_main.c        |  8 +--
 drivers/net/ethernet/socionext/sni_ave.c      | 11 +----
 drivers/net/phy/phy_device.c                  | 30 ++++++++++++
 include/linux/phy.h                           |  1 +
 10 files changed, 69 insertions(+), 148 deletions(-)

diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c 
b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c
index 4f50f11718f4..dfe03afd00b0 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c
@@ -306,7 +306,6 @@ static int xgene_set_pauseparam(struct net_device *ndev,
 {
        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
        struct phy_device *phydev = ndev->phydev;
-       u32 oldadv, newadv;
 
        if (phy_interface_mode_is_rgmii(pdata->phy_mode) ||
            pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
@@ -322,29 +321,12 @@ static int xgene_set_pauseparam(struct net_device *ndev,
                pdata->tx_pause = pp->tx_pause;
                pdata->rx_pause = pp->rx_pause;
 
-               oldadv = phydev->advertising;
-               newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
+               phy_set_asym_pause(phydev, pp->rx_pause,  pp->tx_pause);
 
-               if (pp->rx_pause)
-                       newadv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
-
-               if (pp->tx_pause)
-                       newadv ^= ADVERTISED_Asym_Pause;
-
-               if (oldadv ^ newadv) {
-                       phydev->advertising = newadv;
-
-                       if (phydev->autoneg)
-                               return phy_start_aneg(phydev);
-
-                       if (!pp->autoneg) {
-                               pdata->mac_ops->flowctl_tx(pdata,
-                                                          pdata->tx_pause);
-                               pdata->mac_ops->flowctl_rx(pdata,
-                                                          pdata->rx_pause);
-                       }
+               if (!pp->autoneg) {
+                       pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause);
+                       pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause);
                }
-
        } else {
                if (pp->autoneg)
                        return -EINVAL;
diff --git a/drivers/net/ethernet/aurora/nb8800.c 
b/drivers/net/ethernet/aurora/nb8800.c
index c8d1f8fa4713..6f56276015a4 100644
--- a/drivers/net/ethernet/aurora/nb8800.c
+++ b/drivers/net/ethernet/aurora/nb8800.c
@@ -935,18 +935,11 @@ static void nb8800_pause_adv(struct net_device *dev)
 {
        struct nb8800_priv *priv = netdev_priv(dev);
        struct phy_device *phydev = dev->phydev;
-       u32 adv = 0;
 
        if (!phydev)
                return;
 
-       if (priv->pause_rx)
-               adv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
-       if (priv->pause_tx)
-               adv ^= ADVERTISED_Asym_Pause;
-
-       phydev->supported |= adv;
-       phydev->advertising |= adv;
+       phy_set_asym_pause(phydev, priv->pause_rx, priv->pause_tx);
 }
 
 static int nb8800_open(struct net_device *dev)
diff --git a/drivers/net/ethernet/broadcom/tg3.c 
b/drivers/net/ethernet/broadcom/tg3.c
index 193e990fac7a..b2a3d008e1df 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12492,7 +12492,6 @@ static int tg3_set_pauseparam(struct net_device *dev, 
struct ethtool_pauseparam
                tg3_warn_mgmt_link_flap(tp);
 
        if (tg3_flag(tp, USE_PHYLIB)) {
-               u32 newadv;
                struct phy_device *phydev;
 
                phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
@@ -12503,20 +12502,16 @@ static int tg3_set_pauseparam(struct net_device *dev, 
struct ethtool_pauseparam
                        return -EINVAL;
 
                tp->link_config.flowctrl = 0;
+               phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
                if (epause->rx_pause) {
                        tp->link_config.flowctrl |= FLOW_CTRL_RX;
 
                        if (epause->tx_pause) {
                                tp->link_config.flowctrl |= FLOW_CTRL_TX;
-                               newadv = ADVERTISED_Pause;
-                       } else
-                               newadv = ADVERTISED_Pause |
-                                        ADVERTISED_Asym_Pause;
+                       }
                } else if (epause->tx_pause) {
                        tp->link_config.flowctrl |= FLOW_CTRL_TX;
-                       newadv = ADVERTISED_Asym_Pause;
-               } else
-                       newadv = 0;
+               }
 
                if (epause->autoneg)
                        tg3_flag_set(tp, PAUSE_AUTONEG);
@@ -12524,33 +12519,19 @@ static int tg3_set_pauseparam(struct net_device *dev, 
struct ethtool_pauseparam
                        tg3_flag_clear(tp, PAUSE_AUTONEG);
 
                if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
-                       u32 oldadv = phydev->advertising &
-                                    (ADVERTISED_Pause | ADVERTISED_Asym_Pause);
-                       if (oldadv != newadv) {
-                               phydev->advertising &=
-                                       ~(ADVERTISED_Pause |
-                                         ADVERTISED_Asym_Pause);
-                               phydev->advertising |= newadv;
-                               if (phydev->autoneg) {
-                                       /*
-                                        * Always renegotiate the link to
-                                        * inform our link partner of our
-                                        * flow control settings, even if the
-                                        * flow control is forced.  Let
-                                        * tg3_adjust_link() do the final
-                                        * flow control setup.
-                                        */
-                                       return phy_start_aneg(phydev);
-                               }
+                       if (phydev->autoneg) {
+                               /* phy_set_asym_pause() will
+                                * renegotiate the link to inform our
+                                * link partner of our flow control
+                                * settings, even if the flow control
+                                * is forced.  Let tg3_adjust_link()
+                                * do the final flow control setup.
+                                */
+                               return 0;
                        }
 
                        if (!epause->autoneg)
                                tg3_setup_flow_control(tp, 0, 0);
-               } else {
-                       tp->link_config.advertising &=
-                                       ~(ADVERTISED_Pause |
-                                         ADVERTISED_Asym_Pause);
-                       tp->link_config.advertising |= newadv;
                }
        } else {
                int irq_sync = 0;
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c 
b/drivers/net/ethernet/faraday/ftgmac100.c
index 3f319ee66ab4..d8ead7e4177e 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -1219,22 +1219,11 @@ static int ftgmac100_set_pauseparam(struct net_device 
*netdev,
        priv->tx_pause = pause->tx_pause;
        priv->rx_pause = pause->rx_pause;
 
-       if (phydev) {
-               phydev->advertising &= ~ADVERTISED_Pause;
-               phydev->advertising &= ~ADVERTISED_Asym_Pause;
+       if (phydev)
+               phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_pause);
 
-               if (pause->rx_pause) {
-                       phydev->advertising |= ADVERTISED_Pause;
-                       phydev->advertising |= ADVERTISED_Asym_Pause;
-               }
-
-               if (pause->tx_pause)
-                       phydev->advertising ^= ADVERTISED_Asym_Pause;
-       }
        if (netif_running(netdev)) {
-               if (phydev && priv->aneg_pause)
-                       phy_start_aneg(phydev);
-               else
+               if (!(phydev && priv->aneg_pause))
                        ftgmac100_config_pause(priv);
        }
 
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c 
b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
index 3184c8f7cdd0..1f8cdbc4378c 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
@@ -210,29 +210,8 @@ static int dpaa_set_pauseparam(struct net_device *net_dev,
        /* Determine the sym/asym advertised PAUSE capabilities from the desired
         * rx/tx pause settings.
         */
-       newadv = 0;
-       if (epause->rx_pause)
-               newadv = ADVERTISED_Pause | ADVERTISED_Asym_Pause;
-       if (epause->tx_pause)
-               newadv ^= ADVERTISED_Asym_Pause;
 
-       oldadv = phydev->advertising &
-                       (ADVERTISED_Pause | ADVERTISED_Asym_Pause);
-
-       /* If there are differences between the old and the new advertised
-        * values, restart PHY autonegotiation and advertise the new values.
-        */
-       if (oldadv != newadv) {
-               phydev->advertising &= ~(ADVERTISED_Pause
-                               | ADVERTISED_Asym_Pause);
-               phydev->advertising |= newadv;
-               if (phydev->autoneg) {
-                       err = phy_start_aneg(phydev);
-                       if (err < 0)
-                               netdev_err(net_dev, "phy_start_aneg() = %d\n",
-                                          err);
-               }
-       }
+       phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
 
        fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
        err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c 
b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 395a5266ea30..3545e8f715f2 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -503,7 +503,6 @@ static int gfar_spauseparam(struct net_device *dev,
        struct gfar_private *priv = netdev_priv(dev);
        struct phy_device *phydev = dev->phydev;
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
-       u32 oldadv, newadv;
 
        if (!phydev)
                return -ENODEV;
@@ -514,54 +513,36 @@ static int gfar_spauseparam(struct net_device *dev,
                return -EINVAL;
 
        priv->rx_pause_en = priv->tx_pause_en = 0;
+       phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
        if (epause->rx_pause) {
                priv->rx_pause_en = 1;
 
                if (epause->tx_pause) {
                        priv->tx_pause_en = 1;
-                       /* FLOW_CTRL_RX & TX */
-                       newadv = ADVERTISED_Pause;
-               } else  /* FLOW_CTLR_RX */
-                       newadv = ADVERTISED_Pause | ADVERTISED_Asym_Pause;
+               }
        } else if (epause->tx_pause) {
                priv->tx_pause_en = 1;
-               /* FLOW_CTLR_TX */
-               newadv = ADVERTISED_Asym_Pause;
-       } else
-               newadv = 0;
+       }
 
        if (epause->autoneg)
                priv->pause_aneg_en = 1;
        else
                priv->pause_aneg_en = 0;
 
-       oldadv = phydev->advertising &
-               (ADVERTISED_Pause | ADVERTISED_Asym_Pause);
-       if (oldadv != newadv) {
-               phydev->advertising &=
-                       ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
-               phydev->advertising |= newadv;
-               if (phydev->autoneg)
-                       /* inform link partner of our
-                        * new flow ctrl settings
-                        */
-                       return phy_start_aneg(phydev);
-
-               if (!epause->autoneg) {
-                       u32 tempval;
-                       tempval = gfar_read(&regs->maccfg1);
-                       tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
-
-                       priv->tx_actual_en = 0;
-                       if (priv->tx_pause_en) {
-                               priv->tx_actual_en = 1;
-                               tempval |= MACCFG1_TX_FLOW;
-                       }
+       if (!epause->autoneg) {
+               u32 tempval = gfar_read(&regs->maccfg1);
 
-                       if (priv->rx_pause_en)
-                               tempval |= MACCFG1_RX_FLOW;
-                       gfar_write(&regs->maccfg1, tempval);
+               tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
+
+               priv->tx_actual_en = 0;
+               if (priv->tx_pause_en) {
+                       priv->tx_actual_en = 1;
+                       tempval |= MACCFG1_TX_FLOW;
                }
+
+               if (priv->rx_pause_en)
+                       tempval |= MACCFG1_RX_FLOW;
+               gfar_write(&regs->maccfg1, tempval);
        }
 
        return 0;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c 
b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index c56db06b63e0..cf18608669f5 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -5228,13 +5228,7 @@ static void hclge_set_flowctrl_adv(struct hclge_dev 
*hdev, u32 rx_en, u32 tx_en)
        if (!phydev)
                return;
 
-       phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
-
-       if (rx_en)
-               phydev->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
-
-       if (tx_en)
-               phydev->advertising ^= ADVERTISED_Asym_Pause;
+       phy_set_asym_pause(phydev, rx_en, tx_en);
 }
 
 static int hclge_cfg_pauseparam(struct hclge_dev *hdev, u32 rx_en, u32 tx_en)
diff --git a/drivers/net/ethernet/socionext/sni_ave.c 
b/drivers/net/ethernet/socionext/sni_ave.c
index a50720ec109c..61e6abb966ac 100644
--- a/drivers/net/ethernet/socionext/sni_ave.c
+++ b/drivers/net/ethernet/socionext/sni_ave.c
@@ -461,16 +461,7 @@ static int ave_ethtool_set_pauseparam(struct net_device 
*ndev,
        priv->pause_rx   = pause->rx_pause;
        priv->pause_tx   = pause->tx_pause;
 
-       phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
-       if (pause->rx_pause)
-               phydev->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
-       if (pause->tx_pause)
-               phydev->advertising ^= ADVERTISED_Asym_Pause;
-
-       if (pause->autoneg) {
-               if (netif_running(ndev))
-                       phy_start_aneg(phydev);
-       }
+       phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_pause);
 
        return 0;
 }
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index e657d5ae2ab8..5732d89c8e37 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1810,6 +1810,36 @@ void phy_support_asym_pause(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(phy_support_asym_pause);
 
+/**
+ * phy_set_asym_pause - Configure Pause and Asym Pause
+ * @phydev: target phy_device struct
+ * @rx: Receiver Pause is supported
+ * @tx: Transmit Pause is supported
+ *
+ * Description: Configure advertised Pause support depending on if
+ * transmit and receiver pause is supported. If there has been a
+ * change in adverting, trigger a new autoneg. Generally called from
+ * the set_pauseparam .ndo.
+ */
+void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx)
+{
+       u16 oldadv = phydev->advertising;
+       u16 newadv = oldadv &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+
+       if (rx)
+               newadv |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       if (tx)
+               newadv ^= SUPPORTED_Asym_Pause;
+
+       if (oldadv != newadv) {
+               phydev->advertising = newadv;
+
+               if (phydev->autoneg)
+                       phy_start_aneg(phydev);
+       }
+}
+EXPORT_SYMBOL(phy_set_asym_pause);
+
 static void of_set_phy_supported(struct phy_device *phydev)
 {
        struct device_node *node = phydev->mdio.dev.of_node;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index bc5d6c3f1388..e4062ba7472f 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1052,6 +1052,7 @@ int phy_set_max_speed(struct phy_device *phydev, u32 
max_speed);
 void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode);
 void phy_support_sym_pause(struct phy_device *phydev);
 void phy_support_asym_pause(struct phy_device *phydev);
+void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx);
 
 int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
                       int (*run)(struct phy_device *));
-- 
2.19.0.rc1

Reply via email to