Commit 70814e81 authored by Andrew Lunn's avatar Andrew Lunn Committed by David S. Miller

net: ethernet: Add helper for set_pauseparam for Asym Pause

ethtool can be used to enable/disable pause. Add a helper to configure
the PHY when asym pause is supported.
Signed-off-by: default avatarAndrew Lunn <andrew@lunn.ch>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c306ad36
...@@ -306,7 +306,6 @@ static int xgene_set_pauseparam(struct net_device *ndev, ...@@ -306,7 +306,6 @@ static int xgene_set_pauseparam(struct net_device *ndev,
{ {
struct xgene_enet_pdata *pdata = netdev_priv(ndev); struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct phy_device *phydev = ndev->phydev; struct phy_device *phydev = ndev->phydev;
u32 oldadv, newadv;
if (phy_interface_mode_is_rgmii(pdata->phy_mode) || if (phy_interface_mode_is_rgmii(pdata->phy_mode) ||
pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
...@@ -322,29 +321,12 @@ static int xgene_set_pauseparam(struct net_device *ndev, ...@@ -322,29 +321,12 @@ static int xgene_set_pauseparam(struct net_device *ndev,
pdata->tx_pause = pp->tx_pause; pdata->tx_pause = pp->tx_pause;
pdata->rx_pause = pp->rx_pause; pdata->rx_pause = pp->rx_pause;
oldadv = phydev->advertising; phy_set_asym_pause(phydev, pp->rx_pause, pp->tx_pause);
newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_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) { if (!pp->autoneg) {
pdata->mac_ops->flowctl_tx(pdata, pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause);
pdata->tx_pause); pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause);
pdata->mac_ops->flowctl_rx(pdata,
pdata->rx_pause);
}
} }
} else { } else {
if (pp->autoneg) if (pp->autoneg)
return -EINVAL; return -EINVAL;
......
...@@ -935,18 +935,11 @@ static void nb8800_pause_adv(struct net_device *dev) ...@@ -935,18 +935,11 @@ static void nb8800_pause_adv(struct net_device *dev)
{ {
struct nb8800_priv *priv = netdev_priv(dev); struct nb8800_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev; struct phy_device *phydev = dev->phydev;
u32 adv = 0;
if (!phydev) if (!phydev)
return; return;
if (priv->pause_rx) phy_set_asym_pause(phydev, priv->pause_rx, priv->pause_tx);
adv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
if (priv->pause_tx)
adv ^= ADVERTISED_Asym_Pause;
phydev->supported |= adv;
phydev->advertising |= adv;
} }
static int nb8800_open(struct net_device *dev) static int nb8800_open(struct net_device *dev)
......
...@@ -12492,7 +12492,6 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam ...@@ -12492,7 +12492,6 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
tg3_warn_mgmt_link_flap(tp); tg3_warn_mgmt_link_flap(tp);
if (tg3_flag(tp, USE_PHYLIB)) { if (tg3_flag(tp, USE_PHYLIB)) {
u32 newadv;
struct phy_device *phydev; struct phy_device *phydev;
phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); 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 ...@@ -12503,20 +12502,16 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
return -EINVAL; return -EINVAL;
tp->link_config.flowctrl = 0; tp->link_config.flowctrl = 0;
phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
if (epause->rx_pause) { if (epause->rx_pause) {
tp->link_config.flowctrl |= FLOW_CTRL_RX; tp->link_config.flowctrl |= FLOW_CTRL_RX;
if (epause->tx_pause) { if (epause->tx_pause) {
tp->link_config.flowctrl |= FLOW_CTRL_TX; tp->link_config.flowctrl |= FLOW_CTRL_TX;
newadv = ADVERTISED_Pause; }
} else
newadv = ADVERTISED_Pause |
ADVERTISED_Asym_Pause;
} else if (epause->tx_pause) { } else if (epause->tx_pause) {
tp->link_config.flowctrl |= FLOW_CTRL_TX; tp->link_config.flowctrl |= FLOW_CTRL_TX;
newadv = ADVERTISED_Asym_Pause; }
} else
newadv = 0;
if (epause->autoneg) if (epause->autoneg)
tg3_flag_set(tp, PAUSE_AUTONEG); tg3_flag_set(tp, PAUSE_AUTONEG);
...@@ -12524,33 +12519,19 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam ...@@ -12524,33 +12519,19 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
tg3_flag_clear(tp, PAUSE_AUTONEG); tg3_flag_clear(tp, PAUSE_AUTONEG);
if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) { 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) { if (phydev->autoneg) {
/* /* phy_set_asym_pause() will
* Always renegotiate the link to * renegotiate the link to inform our
* inform our link partner of our * link partner of our flow control
* flow control settings, even if the * settings, even if the flow control
* flow control is forced. Let * is forced. Let tg3_adjust_link()
* tg3_adjust_link() do the final * do the final flow control setup.
* flow control setup.
*/ */
return phy_start_aneg(phydev); return 0;
}
} }
if (!epause->autoneg) if (!epause->autoneg)
tg3_setup_flow_control(tp, 0, 0); tg3_setup_flow_control(tp, 0, 0);
} else {
tp->link_config.advertising &=
~(ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
tp->link_config.advertising |= newadv;
} }
} else { } else {
int irq_sync = 0; int irq_sync = 0;
......
...@@ -1219,22 +1219,11 @@ static int ftgmac100_set_pauseparam(struct net_device *netdev, ...@@ -1219,22 +1219,11 @@ static int ftgmac100_set_pauseparam(struct net_device *netdev,
priv->tx_pause = pause->tx_pause; priv->tx_pause = pause->tx_pause;
priv->rx_pause = pause->rx_pause; priv->rx_pause = pause->rx_pause;
if (phydev) { if (phydev)
phydev->advertising &= ~ADVERTISED_Pause; phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_pause);
phydev->advertising &= ~ADVERTISED_Asym_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 (netif_running(netdev)) {
if (phydev && priv->aneg_pause) if (!(phydev && priv->aneg_pause))
phy_start_aneg(phydev);
else
ftgmac100_config_pause(priv); ftgmac100_config_pause(priv);
} }
......
...@@ -210,29 +210,8 @@ static int dpaa_set_pauseparam(struct net_device *net_dev, ...@@ -210,29 +210,8 @@ static int dpaa_set_pauseparam(struct net_device *net_dev,
/* Determine the sym/asym advertised PAUSE capabilities from the desired /* Determine the sym/asym advertised PAUSE capabilities from the desired
* rx/tx pause settings. * 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 & phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
(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);
}
}
fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
......
...@@ -503,7 +503,6 @@ static int gfar_spauseparam(struct net_device *dev, ...@@ -503,7 +503,6 @@ static int gfar_spauseparam(struct net_device *dev,
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev; struct phy_device *phydev = dev->phydev;
struct gfar __iomem *regs = priv->gfargrp[0].regs; struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 oldadv, newadv;
if (!phydev) if (!phydev)
return -ENODEV; return -ENODEV;
...@@ -514,42 +513,25 @@ static int gfar_spauseparam(struct net_device *dev, ...@@ -514,42 +513,25 @@ static int gfar_spauseparam(struct net_device *dev,
return -EINVAL; return -EINVAL;
priv->rx_pause_en = priv->tx_pause_en = 0; priv->rx_pause_en = priv->tx_pause_en = 0;
phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
if (epause->rx_pause) { if (epause->rx_pause) {
priv->rx_pause_en = 1; priv->rx_pause_en = 1;
if (epause->tx_pause) { if (epause->tx_pause) {
priv->tx_pause_en = 1; 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) { } else if (epause->tx_pause) {
priv->tx_pause_en = 1; priv->tx_pause_en = 1;
/* FLOW_CTLR_TX */ }
newadv = ADVERTISED_Asym_Pause;
} else
newadv = 0;
if (epause->autoneg) if (epause->autoneg)
priv->pause_aneg_en = 1; priv->pause_aneg_en = 1;
else else
priv->pause_aneg_en = 0; 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) { if (!epause->autoneg) {
u32 tempval; u32 tempval = gfar_read(&regs->maccfg1);
tempval = gfar_read(&regs->maccfg1);
tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
priv->tx_actual_en = 0; priv->tx_actual_en = 0;
...@@ -562,7 +544,6 @@ static int gfar_spauseparam(struct net_device *dev, ...@@ -562,7 +544,6 @@ static int gfar_spauseparam(struct net_device *dev,
tempval |= MACCFG1_RX_FLOW; tempval |= MACCFG1_RX_FLOW;
gfar_write(&regs->maccfg1, tempval); gfar_write(&regs->maccfg1, tempval);
} }
}
return 0; return 0;
} }
......
...@@ -5228,13 +5228,7 @@ static void hclge_set_flowctrl_adv(struct hclge_dev *hdev, u32 rx_en, u32 tx_en) ...@@ -5228,13 +5228,7 @@ static void hclge_set_flowctrl_adv(struct hclge_dev *hdev, u32 rx_en, u32 tx_en)
if (!phydev) if (!phydev)
return; return;
phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); phy_set_asym_pause(phydev, rx_en, tx_en);
if (rx_en)
phydev->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
if (tx_en)
phydev->advertising ^= ADVERTISED_Asym_Pause;
} }
static int hclge_cfg_pauseparam(struct hclge_dev *hdev, u32 rx_en, u32 tx_en) static int hclge_cfg_pauseparam(struct hclge_dev *hdev, u32 rx_en, u32 tx_en)
......
...@@ -461,16 +461,7 @@ static int ave_ethtool_set_pauseparam(struct net_device *ndev, ...@@ -461,16 +461,7 @@ static int ave_ethtool_set_pauseparam(struct net_device *ndev,
priv->pause_rx = pause->rx_pause; priv->pause_rx = pause->rx_pause;
priv->pause_tx = pause->tx_pause; priv->pause_tx = pause->tx_pause;
phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_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);
}
return 0; return 0;
} }
......
...@@ -1810,6 +1810,36 @@ void phy_support_asym_pause(struct phy_device *phydev) ...@@ -1810,6 +1810,36 @@ void phy_support_asym_pause(struct phy_device *phydev)
} }
EXPORT_SYMBOL(phy_support_asym_pause); 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) static void of_set_phy_supported(struct phy_device *phydev)
{ {
struct device_node *node = phydev->mdio.dev.of_node; struct device_node *node = phydev->mdio.dev.of_node;
......
...@@ -1052,6 +1052,7 @@ int phy_set_max_speed(struct phy_device *phydev, u32 max_speed); ...@@ -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_remove_link_mode(struct phy_device *phydev, u32 link_mode);
void phy_support_sym_pause(struct phy_device *phydev); void phy_support_sym_pause(struct phy_device *phydev);
void phy_support_asym_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 phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
int (*run)(struct phy_device *)); int (*run)(struct phy_device *));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment