Commit 76cce0af authored by Tom Lendacky's avatar Tom Lendacky Committed by David S. Miller

amd-xgbe: Improve SFP 100Mbps auto-negotiation

After changing speed to 100Mbps as a result of auto-negotiation (AN),
some 10/100/1000Mbps SFPs indicate a successful link (no faults or loss
of signal), but cannot successfully transmit or receive data.  These
SFPs required an extra auto-negotiation (AN) after the speed change in
order to operate properly.  Add a quirk for these SFPs so that if the
outcome of the AN actually results in changing to a new speed, re-initiate
AN at that new speed.
Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e722ec82
...@@ -331,13 +331,15 @@ static void xgbe_switch_mode(struct xgbe_prv_data *pdata) ...@@ -331,13 +331,15 @@ static void xgbe_switch_mode(struct xgbe_prv_data *pdata)
xgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata)); xgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata));
} }
static void xgbe_set_mode(struct xgbe_prv_data *pdata, static bool xgbe_set_mode(struct xgbe_prv_data *pdata,
enum xgbe_mode mode) enum xgbe_mode mode)
{ {
if (mode == xgbe_cur_mode(pdata)) if (mode == xgbe_cur_mode(pdata))
return; return false;
xgbe_change_mode(pdata, mode); xgbe_change_mode(pdata, mode);
return true;
} }
static bool xgbe_use_mode(struct xgbe_prv_data *pdata, static bool xgbe_use_mode(struct xgbe_prv_data *pdata,
...@@ -1178,21 +1180,23 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) ...@@ -1178,21 +1180,23 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
return 0; return 0;
} }
static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata, bool set_mode)
{ {
int ret; int ret;
mutex_lock(&pdata->an_mutex);
set_bit(XGBE_LINK_INIT, &pdata->dev_state); set_bit(XGBE_LINK_INIT, &pdata->dev_state);
pdata->link_check = jiffies; pdata->link_check = jiffies;
ret = pdata->phy_if.phy_impl.an_config(pdata); ret = pdata->phy_if.phy_impl.an_config(pdata);
if (ret) if (ret)
return ret; goto out;
if (pdata->phy.autoneg != AUTONEG_ENABLE) { if (pdata->phy.autoneg != AUTONEG_ENABLE) {
ret = xgbe_phy_config_fixed(pdata); ret = xgbe_phy_config_fixed(pdata);
if (ret || !pdata->kr_redrv) if (ret || !pdata->kr_redrv)
return ret; goto out;
netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n"); netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n");
} else { } else {
...@@ -1202,24 +1206,27 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) ...@@ -1202,24 +1206,27 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
/* Disable auto-negotiation interrupt */ /* Disable auto-negotiation interrupt */
disable_irq(pdata->an_irq); disable_irq(pdata->an_irq);
/* Start auto-negotiation in a supported mode */ if (set_mode) {
if (xgbe_use_mode(pdata, XGBE_MODE_KR)) { /* Start auto-negotiation in a supported mode */
xgbe_set_mode(pdata, XGBE_MODE_KR); if (xgbe_use_mode(pdata, XGBE_MODE_KR)) {
} else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) { xgbe_set_mode(pdata, XGBE_MODE_KR);
xgbe_set_mode(pdata, XGBE_MODE_KX_2500); } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) {
} else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { xgbe_set_mode(pdata, XGBE_MODE_KX_2500);
xgbe_set_mode(pdata, XGBE_MODE_KX_1000); } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
} else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) { xgbe_set_mode(pdata, XGBE_MODE_KX_1000);
xgbe_set_mode(pdata, XGBE_MODE_SFI); } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) {
} else if (xgbe_use_mode(pdata, XGBE_MODE_X)) { xgbe_set_mode(pdata, XGBE_MODE_SFI);
xgbe_set_mode(pdata, XGBE_MODE_X); } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) {
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) { xgbe_set_mode(pdata, XGBE_MODE_X);
xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000); } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) {
} else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) { xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000);
xgbe_set_mode(pdata, XGBE_MODE_SGMII_100); } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) {
} else { xgbe_set_mode(pdata, XGBE_MODE_SGMII_100);
enable_irq(pdata->an_irq); } else {
return -EINVAL; enable_irq(pdata->an_irq);
ret = -EINVAL;
goto out;
}
} }
/* Disable and stop any in progress auto-negotiation */ /* Disable and stop any in progress auto-negotiation */
...@@ -1239,16 +1246,7 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) ...@@ -1239,16 +1246,7 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
xgbe_an_init(pdata); xgbe_an_init(pdata);
xgbe_an_restart(pdata); xgbe_an_restart(pdata);
return 0; out:
}
static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
{
int ret;
mutex_lock(&pdata->an_mutex);
ret = __xgbe_phy_config_aneg(pdata);
if (ret) if (ret)
set_bit(XGBE_LINK_ERR, &pdata->dev_state); set_bit(XGBE_LINK_ERR, &pdata->dev_state);
else else
...@@ -1259,6 +1257,16 @@ static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) ...@@ -1259,6 +1257,16 @@ static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
return ret; return ret;
} }
static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
{
return __xgbe_phy_config_aneg(pdata, true);
}
static int xgbe_phy_reconfig_aneg(struct xgbe_prv_data *pdata)
{
return __xgbe_phy_config_aneg(pdata, false);
}
static bool xgbe_phy_aneg_done(struct xgbe_prv_data *pdata) static bool xgbe_phy_aneg_done(struct xgbe_prv_data *pdata)
{ {
return (pdata->an_result == XGBE_AN_COMPLETE); return (pdata->an_result == XGBE_AN_COMPLETE);
...@@ -1315,7 +1323,8 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) ...@@ -1315,7 +1323,8 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata)
pdata->phy.duplex = DUPLEX_FULL; pdata->phy.duplex = DUPLEX_FULL;
xgbe_set_mode(pdata, mode); if (xgbe_set_mode(pdata, mode) && pdata->an_again)
xgbe_phy_reconfig_aneg(pdata);
} }
static void xgbe_phy_status(struct xgbe_prv_data *pdata) static void xgbe_phy_status(struct xgbe_prv_data *pdata)
......
...@@ -902,6 +902,9 @@ static bool xgbe_phy_belfuse_phy_quirks(struct xgbe_prv_data *pdata) ...@@ -902,6 +902,9 @@ static bool xgbe_phy_belfuse_phy_quirks(struct xgbe_prv_data *pdata)
XGBE_BEL_FUSE_VENDOR, XGBE_SFP_BASE_VENDOR_NAME_LEN)) XGBE_BEL_FUSE_VENDOR, XGBE_SFP_BASE_VENDOR_NAME_LEN))
return false; return false;
/* For Bel-Fuse, use the extra AN flag */
pdata->an_again = 1;
if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN], if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN],
XGBE_BEL_FUSE_PARTNO, XGBE_SFP_BASE_VENDOR_PN_LEN)) XGBE_BEL_FUSE_PARTNO, XGBE_SFP_BASE_VENDOR_PN_LEN))
return false; return false;
...@@ -978,6 +981,9 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata) ...@@ -978,6 +981,9 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)
if (phy_data->phydev) if (phy_data->phydev)
return 0; return 0;
/* Clear the extra AN flag */
pdata->an_again = 0;
/* Check for the use of an external PHY */ /* Check for the use of an external PHY */
if (phy_data->phydev_mode == XGBE_MDIO_MODE_NONE) if (phy_data->phydev_mode == XGBE_MDIO_MODE_NONE)
return 0; return 0;
......
...@@ -1261,6 +1261,7 @@ struct xgbe_prv_data { ...@@ -1261,6 +1261,7 @@ struct xgbe_prv_data {
enum xgbe_rx kr_state; enum xgbe_rx kr_state;
enum xgbe_rx kx_state; enum xgbe_rx kx_state;
struct work_struct an_work; struct work_struct an_work;
unsigned int an_again;
unsigned int an_supported; unsigned int an_supported;
unsigned int parallel_detect; unsigned int parallel_detect;
unsigned int fec_ability; unsigned int fec_ability;
......
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