aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet
diff options
context:
space:
mode:
authorLendacky, Thomas <Thomas.Lendacky@amd.com>2015-05-14 11:44:27 -0500
committerDavid S. Miller <davem@davemloft.net>2015-05-15 15:21:43 -0400
commitc1ce2f77366bb7cde153596e9406fc7727d5726c (patch)
tree08bc7a1dd0956ecec94d447335d2eebc419ac980 /drivers/net/ethernet
parentamd-xgbe: Support defining PHY resources in ETH device node (diff)
downloadlinux-dev-c1ce2f77366bb7cde153596e9406fc7727d5726c.tar.xz
linux-dev-c1ce2f77366bb7cde153596e9406fc7727d5726c.zip
amd-xgbe: Fix flow control setting logic
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>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c2
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c29
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-mdio.c73
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h8
4 files changed, 72 insertions, 40 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 401703fc7b4f..e4521799ba9c 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -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);
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
index b24a78c39800..59e090e95c0e 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
@@ -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);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 1ae4bfbd13d3..cea19a37806e 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -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);
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index f535d19da803..63d72a140053 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -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;