Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+43 −34
Original line number Diff line number Diff line
@@ -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,6 +1206,7 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
	/* Disable auto-negotiation interrupt */
	disable_irq(pdata->an_irq);

	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);
@@ -1219,7 +1224,9 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
			xgbe_set_mode(pdata, XGBE_MODE_SGMII_100);
		} else {
			enable_irq(pdata->an_irq);
		return -EINVAL;
			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)
+6 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;