Skip to content

Commit

Permalink
amd-xgbe: Fix flow control setting logic
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Lendacky, Thomas authored and David S. Miller committed May 15, 2015
1 parent 34bfff4 commit c1ce2f7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 40 deletions.
2 changes: 0 additions & 2 deletions drivers/net/ethernet/amd/xgbe/xgbe-drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
29 changes: 17 additions & 12 deletions drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand All @@ -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);

Expand Down
73 changes: 51 additions & 22 deletions drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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");
}
Expand All @@ -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 */
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
8 changes: 4 additions & 4 deletions drivers/net/ethernet/amd/xgbe/xgbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit c1ce2f7

Please sign in to comment.