Commit a17aafa3 authored by David S. Miller's avatar David S. Miller

Merge branch 'bcmgenet-flow-control'

Florian Fainelli says:

====================
net: bcmgenet: support for flow control

This patch series adds support for flow control to the GENET driver, the
first 2 patches remove superfluous code, the 3rd one does re-organize
code a little bit and the 4th one ads the support for flow control
proper.
====================
parents 13807ded 2d8bdf52
......@@ -935,6 +935,48 @@ static int bcmgenet_set_coalesce(struct net_device *dev,
return 0;
}
static void bcmgenet_get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct bcmgenet_priv *priv;
u32 umac_cmd;
priv = netdev_priv(dev);
epause->autoneg = priv->autoneg_pause;
if (netif_carrier_ok(dev)) {
/* report active state when link is up */
umac_cmd = bcmgenet_umac_readl(priv, UMAC_CMD);
epause->tx_pause = !(umac_cmd & CMD_TX_PAUSE_IGNORE);
epause->rx_pause = !(umac_cmd & CMD_RX_PAUSE_IGNORE);
} else {
/* otherwise report stored settings */
epause->tx_pause = priv->tx_pause;
epause->rx_pause = priv->rx_pause;
}
}
static int bcmgenet_set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
if (!dev->phydev)
return -ENODEV;
if (!phy_validate_pause(dev->phydev, epause))
return -EINVAL;
priv->autoneg_pause = !!epause->autoneg;
priv->tx_pause = !!epause->tx_pause;
priv->rx_pause = !!epause->rx_pause;
bcmgenet_phy_pause_set(dev, priv->rx_pause, priv->tx_pause);
return 0;
}
/* standard ethtool support functions. */
enum bcmgenet_stat_type {
BCMGENET_STAT_NETDEV = -1,
......@@ -1587,6 +1629,8 @@ static const struct ethtool_ops bcmgenet_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info,
.get_rxnfc = bcmgenet_get_rxnfc,
.set_rxnfc = bcmgenet_set_rxnfc,
.get_pauseparam = bcmgenet_get_pauseparam,
.set_pauseparam = bcmgenet_set_pauseparam,
};
/* Power down the unimac, based on mode. */
......@@ -3364,6 +3408,8 @@ static int bcmgenet_open(struct net_device *dev)
goto err_irq1;
}
bcmgenet_phy_pause_set(dev, priv->rx_pause, priv->tx_pause);
bcmgenet_netif_start(dev);
netif_tx_start_all_queues(dev);
......@@ -3408,11 +3454,6 @@ static void bcmgenet_netif_stop(struct net_device *dev)
*/
cancel_work_sync(&priv->bcmgenet_irq_work);
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
/* tx reclaim */
bcmgenet_tx_reclaim_all(dev);
bcmgenet_fini_dma(priv);
......@@ -3950,6 +3991,11 @@ static int bcmgenet_probe(struct platform_device *pdev)
spin_lock_init(&priv->lock);
/* Set default pause parameters */
priv->autoneg_pause = 1;
priv->tx_pause = 1;
priv->rx_pause = 1;
SET_NETDEV_DEV(dev, &pdev->dev);
dev_set_drvdata(&pdev->dev, dev);
dev->watchdog_timeo = 2 * HZ;
......
......@@ -594,6 +594,9 @@ struct bcmgenet_priv {
/* other misc variables */
struct bcmgenet_hw_params *hw_params;
unsigned autoneg_pause:1;
unsigned tx_pause:1;
unsigned rx_pause:1;
/* MDIO bus variables */
wait_queue_head_t wq;
......@@ -606,10 +609,6 @@ struct bcmgenet_priv {
bool clk_eee_enabled;
/* PHY device variables */
int old_link;
int old_speed;
int old_duplex;
int old_pause;
phy_interface_t phy_interface;
int phy_addr;
int ext_phy;
......@@ -690,6 +689,7 @@ int bcmgenet_mii_init(struct net_device *dev);
int bcmgenet_mii_config(struct net_device *dev, bool init);
int bcmgenet_mii_probe(struct net_device *dev);
void bcmgenet_mii_exit(struct net_device *dev);
void bcmgenet_phy_pause_set(struct net_device *dev, bool rx, bool tx);
void bcmgenet_phy_power_set(struct net_device *dev, bool enable);
void bcmgenet_mii_setup(struct net_device *dev);
......
......@@ -25,92 +25,80 @@
#include "bcmgenet.h"
/* setup netdev link state when PHY link status change and
* update UMAC and RGMII block when link up
*/
void bcmgenet_mii_setup(struct net_device *dev)
static void bcmgenet_mac_config(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
u32 reg, cmd_bits = 0;
bool status_changed = false;
if (priv->old_link != phydev->link) {
status_changed = true;
priv->old_link = phydev->link;
}
/* speed */
if (phydev->speed == SPEED_1000)
cmd_bits = CMD_SPEED_1000;
else if (phydev->speed == SPEED_100)
cmd_bits = CMD_SPEED_100;
else
cmd_bits = CMD_SPEED_10;
cmd_bits <<= CMD_SPEED_SHIFT;
if (phydev->link) {
/* check speed/duplex/pause changes */
if (priv->old_speed != phydev->speed) {
status_changed = true;
priv->old_speed = phydev->speed;
}
/* duplex */
if (phydev->duplex != DUPLEX_FULL) {
cmd_bits |= CMD_HD_EN |
CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
} else {
/* pause capability defaults to Symmetric */
if (priv->autoneg_pause) {
bool tx_pause = 0, rx_pause = 0;
if (priv->old_duplex != phydev->duplex) {
status_changed = true;
priv->old_duplex = phydev->duplex;
}
if (phydev->autoneg)
phy_get_pause(phydev, &tx_pause, &rx_pause);
if (priv->old_pause != phydev->pause) {
status_changed = true;
priv->old_pause = phydev->pause;
if (!tx_pause)
cmd_bits |= CMD_TX_PAUSE_IGNORE;
if (!rx_pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE;
}
/* done if nothing has changed */
if (!status_changed)
return;
/* speed */
if (phydev->speed == SPEED_1000)
cmd_bits = CMD_SPEED_1000;
else if (phydev->speed == SPEED_100)
cmd_bits = CMD_SPEED_100;
else
cmd_bits = CMD_SPEED_10;
cmd_bits <<= CMD_SPEED_SHIFT;
/* duplex */
if (phydev->duplex != DUPLEX_FULL)
cmd_bits |= CMD_HD_EN;
/* pause capability */
if (!phydev->pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
/*
* Program UMAC and RGMII block based on established
* link speed, duplex, and pause. The speed set in
* umac->cmd tell RGMII block which clock to use for
* transmit -- 25MHz(100Mbps) or 125MHz(1Gbps).
* Receive clock is provided by the PHY.
*/
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~OOB_DISABLE;
reg |= RGMII_LINK;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
/* Manual override */
if (!priv->rx_pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE;
if (!priv->tx_pause)
cmd_bits |= CMD_TX_PAUSE_IGNORE;
}
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
CMD_HD_EN |
CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
reg |= cmd_bits;
if (reg & CMD_SW_RESET) {
reg &= ~CMD_SW_RESET;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
udelay(2);
reg |= CMD_TX_EN | CMD_RX_EN;
}
/* Program UMAC and RGMII block based on established
* link speed, duplex, and pause. The speed set in
* umac->cmd tell RGMII block which clock to use for
* transmit -- 25MHz(100Mbps) or 125MHz(1Gbps).
* Receive clock is provided by the PHY.
*/
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~OOB_DISABLE;
reg |= RGMII_LINK;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
CMD_HD_EN |
CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
reg |= cmd_bits;
if (reg & CMD_SW_RESET) {
reg &= ~CMD_SW_RESET;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
} else {
/* done if nothing has changed */
if (!status_changed)
return;
/* needed for MoCA fixed PHY to reflect correct link status */
netif_carrier_off(dev);
udelay(2);
reg |= CMD_TX_EN | CMD_RX_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
}
/* setup netdev link state when PHY link status change and
* update UMAC and RGMII block when link up
*/
void bcmgenet_mii_setup(struct net_device *dev)
{
struct phy_device *phydev = dev->phydev;
if (phydev->link)
bcmgenet_mac_config(dev);
phy_print_status(phydev);
}
......@@ -130,6 +118,21 @@ static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
return 0;
}
void bcmgenet_phy_pause_set(struct net_device *dev, bool rx, bool tx)
{
struct phy_device *phydev = dev->phydev;
linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->advertising, rx);
linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->advertising,
rx | tx);
phy_start_aneg(phydev);
mutex_lock(&phydev->lock);
if (phydev->link)
bcmgenet_mac_config(dev);
mutex_unlock(&phydev->lock);
}
void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
......@@ -297,12 +300,6 @@ int bcmgenet_mii_probe(struct net_device *dev)
if (priv->internal_phy)
phy_flags = priv->gphy_rev;
/* Initialize link state variables that bcmgenet_mii_setup() uses */
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
/* This is an ugly quirk but we have not been correctly interpreting
* the phy_interface values and we have done that across different
* drivers, so at least we are consistent in our mistakes.
......@@ -386,8 +383,6 @@ int bcmgenet_mii_probe(struct net_device *dev)
return ret;
}
linkmode_copy(phydev->advertising, phydev->supported);
/* The internal PHY has its link interrupts routed to the
* Ethernet MAC ISRs. On GENETv5 there is a hardware issue
* that prevents the signaling of link UP interrupts when
......
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