summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/amd
diff options
context:
space:
mode:
authorTom Lendacky <thomas.lendacky@amd.com>2018-05-23 19:39:47 +0300
committerDavid S. Miller <davem@davemloft.net>2018-05-23 23:33:01 +0300
commit76cce0af85a0d5d8abef8b60eece5798ea7eea5a (patch)
tree90b202a343f7866142fd8b2d84ab67d6c4542af9 /drivers/net/ethernet/amd
parente722ec82374b7a0c0cfa954e4f780221139c5f93 (diff)
downloadlinux-76cce0af85a0d5d8abef8b60eece5798ea7eea5a.tar.xz
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: Tom Lendacky <thomas.lendacky@amd.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/amd')
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-mdio.c77
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c6
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h1
3 files changed, 50 insertions, 34 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 450b89cd9eeb..4b5d625de8f0 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -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));
}
-static void xgbe_set_mode(struct xgbe_prv_data *pdata,
+static bool xgbe_set_mode(struct xgbe_prv_data *pdata,
enum xgbe_mode mode)
{
if (mode == xgbe_cur_mode(pdata))
- return;
+ return false;
xgbe_change_mode(pdata, mode);
+
+ return true;
}
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)
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;
+ mutex_lock(&pdata->an_mutex);
+
set_bit(XGBE_LINK_INIT, &pdata->dev_state);
pdata->link_check = jiffies;
ret = pdata->phy_if.phy_impl.an_config(pdata);
if (ret)
- return ret;
+ goto out;
if (pdata->phy.autoneg != AUTONEG_ENABLE) {
ret = xgbe_phy_config_fixed(pdata);
if (ret || !pdata->kr_redrv)
- return ret;
+ goto out;
netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n");
} else {
@@ -1202,24 +1206,27 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
/* Disable auto-negotiation interrupt */
disable_irq(pdata->an_irq);
- /* Start auto-negotiation in a supported mode */
- if (xgbe_use_mode(pdata, XGBE_MODE_KR)) {
- xgbe_set_mode(pdata, XGBE_MODE_KR);
- } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) {
- xgbe_set_mode(pdata, XGBE_MODE_KX_2500);
- } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
- xgbe_set_mode(pdata, XGBE_MODE_KX_1000);
- } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) {
- xgbe_set_mode(pdata, XGBE_MODE_SFI);
- } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) {
- xgbe_set_mode(pdata, XGBE_MODE_X);
- } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) {
- xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000);
- } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) {
- xgbe_set_mode(pdata, XGBE_MODE_SGMII_100);
- } else {
- enable_irq(pdata->an_irq);
- return -EINVAL;
+ if (set_mode) {
+ /* Start auto-negotiation in a supported mode */
+ if (xgbe_use_mode(pdata, XGBE_MODE_KR)) {
+ xgbe_set_mode(pdata, XGBE_MODE_KR);
+ } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) {
+ xgbe_set_mode(pdata, XGBE_MODE_KX_2500);
+ } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
+ xgbe_set_mode(pdata, XGBE_MODE_KX_1000);
+ } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) {
+ xgbe_set_mode(pdata, XGBE_MODE_SFI);
+ } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) {
+ xgbe_set_mode(pdata, XGBE_MODE_X);
+ } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) {
+ xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000);
+ } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) {
+ xgbe_set_mode(pdata, XGBE_MODE_SGMII_100);
+ } else {
+ enable_irq(pdata->an_irq);
+ ret = -EINVAL;
+ goto out;
+ }
}
/* Disable and stop any in progress auto-negotiation */
@@ -1239,16 +1246,7 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
xgbe_an_init(pdata);
xgbe_an_restart(pdata);
- return 0;
-}
-
-static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
-{
- int ret;
-
- mutex_lock(&pdata->an_mutex);
-
- ret = __xgbe_phy_config_aneg(pdata);
+out:
if (ret)
set_bit(XGBE_LINK_ERR, &pdata->dev_state);
else
@@ -1259,6 +1257,16 @@ static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
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)
{
return (pdata->an_result == XGBE_AN_COMPLETE);
@@ -1315,7 +1323,8 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata)
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)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
index 194a5696a82c..3ceb4f95ca7c 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
@@ -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))
return false;
+ /* For Bel-Fuse, use the extra AN flag */
+ pdata->an_again = 1;
+
if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN],
XGBE_BEL_FUSE_PARTNO, XGBE_SFP_BASE_VENDOR_PN_LEN))
return false;
@@ -978,6 +981,9 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)
if (phy_data->phydev)
return 0;
+ /* Clear the extra AN flag */
+ pdata->an_again = 0;
+
/* Check for the use of an external PHY */
if (phy_data->phydev_mode == XGBE_MDIO_MODE_NONE)
return 0;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 7a412cfa8e40..47bcbcf58048 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -1261,6 +1261,7 @@ struct xgbe_prv_data {
enum xgbe_rx kr_state;
enum xgbe_rx kx_state;
struct work_struct an_work;
+ unsigned int an_again;
unsigned int an_supported;
unsigned int parallel_detect;
unsigned int fec_ability;