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>
This commit is contained in:
parent
34bfff404c
commit
c1ce2f7736
|
@ -782,8 +782,6 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
|
||||||
{
|
{
|
||||||
pdata->phy_link = -1;
|
pdata->phy_link = -1;
|
||||||
pdata->phy_speed = SPEED_UNKNOWN;
|
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);
|
return pdata->phy_if.phy_reset(pdata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,9 +247,9 @@ static void xgbe_get_pauseparam(struct net_device *netdev,
|
||||||
|
|
||||||
DBGPR("-->xgbe_get_pauseparam\n");
|
DBGPR("-->xgbe_get_pauseparam\n");
|
||||||
|
|
||||||
pause->autoneg = pdata->pause_autoneg;
|
pause->autoneg = pdata->phy.pause_autoneg;
|
||||||
pause->tx_pause = pdata->tx_pause;
|
pause->tx_pause = pdata->phy.tx_pause;
|
||||||
pause->rx_pause = pdata->rx_pause;
|
pause->rx_pause = pdata->phy.rx_pause;
|
||||||
|
|
||||||
DBGPR("<--xgbe_get_pauseparam\n");
|
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",
|
DBGPR(" autoneg = %d, tx_pause = %d, rx_pause = %d\n",
|
||||||
pause->autoneg, pause->tx_pause, pause->rx_pause);
|
pause->autoneg, pause->tx_pause, pause->rx_pause);
|
||||||
|
|
||||||
pdata->pause_autoneg = pause->autoneg;
|
if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE))
|
||||||
if (pause->autoneg) {
|
return -EINVAL;
|
||||||
|
|
||||||
|
pdata->phy.pause_autoneg = pause->autoneg;
|
||||||
|
pdata->phy.tx_pause = pause->tx_pause;
|
||||||
|
pdata->phy.rx_pause = pause->rx_pause;
|
||||||
|
|
||||||
|
pdata->phy.advertising &= ~ADVERTISED_Pause;
|
||||||
|
pdata->phy.advertising &= ~ADVERTISED_Asym_Pause;
|
||||||
|
|
||||||
|
if (pause->rx_pause) {
|
||||||
pdata->phy.advertising |= ADVERTISED_Pause;
|
pdata->phy.advertising |= ADVERTISED_Pause;
|
||||||
pdata->phy.advertising |= ADVERTISED_Asym_Pause;
|
pdata->phy.advertising |= ADVERTISED_Asym_Pause;
|
||||||
|
|
||||||
} else {
|
|
||||||
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->tx_pause)
|
||||||
|
pdata->phy.advertising ^= ADVERTISED_Asym_Pause;
|
||||||
|
|
||||||
if (netif_running(netdev))
|
if (netif_running(netdev))
|
||||||
ret = pdata->phy_if.phy_config_aneg(pdata);
|
ret = pdata->phy_if.phy_config_aneg(pdata);
|
||||||
|
|
||||||
|
|
|
@ -737,6 +737,18 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata)
|
||||||
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg);
|
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)
|
static const char *xgbe_phy_speed_string(int speed)
|
||||||
{
|
{
|
||||||
switch (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",
|
"Link is Up - %s/%s - flow control %s\n",
|
||||||
xgbe_phy_speed_string(pdata->phy.speed),
|
xgbe_phy_speed_string(pdata->phy.speed),
|
||||||
pdata->phy.duplex == DUPLEX_FULL ? "Full" : "Half",
|
pdata->phy.duplex == DUPLEX_FULL ? "Full" : "Half",
|
||||||
pdata->phy.pause ? "rx/tx" : "off");
|
xgbe_phy_fc_string(pdata));
|
||||||
else
|
else
|
||||||
netdev_info(pdata->netdev, "Link is Down\n");
|
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) {
|
if (pdata->phy.link) {
|
||||||
/* Flow control support */
|
/* Flow control support */
|
||||||
if (pdata->pause_autoneg) {
|
pdata->pause_autoneg = pdata->phy.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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->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->hw_if.config_rx_flow_control(pdata);
|
||||||
pdata->phy_rx_pause = pdata->rx_pause;
|
pdata->rx_pause = pdata->phy.rx_pause;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Speed support */
|
/* Speed support */
|
||||||
|
@ -835,9 +841,6 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
|
||||||
if (pdata->phy.duplex != DUPLEX_FULL)
|
if (pdata->phy.duplex != DUPLEX_FULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
pdata->phy.pause = 0;
|
|
||||||
pdata->phy.asym_pause = 0;
|
|
||||||
|
|
||||||
return 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.duplex = DUPLEX_FULL;
|
||||||
pdata->phy.pause = 0;
|
|
||||||
pdata->phy.asym_pause = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xgbe_phy_status_aneg(struct xgbe_prv_data *pdata)
|
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)
|
if (lp_reg & 0x800)
|
||||||
pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
|
pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
|
||||||
|
|
||||||
ad_reg &= lp_reg;
|
if (pdata->phy.pause_autoneg) {
|
||||||
pdata->phy.pause = (ad_reg & 0x400) ? 1 : 0;
|
/* Set flow control based on auto-negotiation result */
|
||||||
pdata->phy.asym_pause = (ad_reg & 0x800) ? 1 : 0;
|
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 */
|
/* Compare Advertisement and Link Partner register 2 */
|
||||||
ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
|
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.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))
|
if (netif_msg_drv(pdata))
|
||||||
xgbe_dump_phy_registers(pdata);
|
xgbe_dump_phy_registers(pdata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,10 +539,12 @@ struct xgbe_phy {
|
||||||
int autoneg;
|
int autoneg;
|
||||||
int speed;
|
int speed;
|
||||||
int duplex;
|
int duplex;
|
||||||
int pause;
|
|
||||||
int asym_pause;
|
|
||||||
|
|
||||||
int link;
|
int link;
|
||||||
|
|
||||||
|
int pause_autoneg;
|
||||||
|
int tx_pause;
|
||||||
|
int rx_pause;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct xgbe_mmc_stats {
|
struct xgbe_mmc_stats {
|
||||||
|
@ -910,8 +912,6 @@ struct xgbe_prv_data {
|
||||||
phy_interface_t phy_mode;
|
phy_interface_t phy_mode;
|
||||||
int phy_link;
|
int phy_link;
|
||||||
int phy_speed;
|
int phy_speed;
|
||||||
unsigned int phy_tx_pause;
|
|
||||||
unsigned int phy_rx_pause;
|
|
||||||
|
|
||||||
/* MDIO/PHY related settings */
|
/* MDIO/PHY related settings */
|
||||||
struct xgbe_phy phy;
|
struct xgbe_phy phy;
|
||||||
|
|
Loading…
Reference in New Issue