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

Commit 46ed892b authored by Yuiko Oshino's avatar Yuiko Oshino Committed by Greg Kroah-Hartman
Browse files

smsc75xx: Add workaround for gigabit link up hardware errata.



[ Upstream commit d461e3da905332189aad546b2ad9adbe6071c7cc ]

In certain conditions, the device may not be able to link in gigabit mode. This software workaround ensures that the device will not enter the failure state.

Fixes: d0cad871 ("SMSC75XX USB 2.0 Gigabit Ethernet Devices")
Signed-off-by: default avatarYuiko Oshino <yuiko.oshino@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarSasha Levin <alexander.levin@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 766c02b6
Loading
Loading
Loading
Loading
+62 −0
Original line number Diff line number Diff line
@@ -81,6 +81,9 @@ static bool turbo_mode = true;
module_param(turbo_mode, bool, 0644);
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");

static int smsc75xx_link_ok_nopm(struct usbnet *dev);
static int smsc75xx_phy_gig_workaround(struct usbnet *dev);

static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index,
					    u32 *data, int in_pm)
{
@@ -840,6 +843,9 @@ static int smsc75xx_phy_initialize(struct usbnet *dev)
		return -EIO;
	}

	/* phy workaround for gig link */
	smsc75xx_phy_gig_workaround(dev);

	smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
		ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
		ADVERTISE_PAUSE_ASYM);
@@ -978,6 +984,62 @@ static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm)
	return -EIO;
}

static int smsc75xx_phy_gig_workaround(struct usbnet *dev)
{
	struct mii_if_info *mii = &dev->mii;
	int ret = 0, timeout = 0;
	u32 buf, link_up = 0;

	/* Set the phy in Gig loopback */
	smsc75xx_mdio_write(dev->net, mii->phy_id, MII_BMCR, 0x4040);

	/* Wait for the link up */
	do {
		link_up = smsc75xx_link_ok_nopm(dev);
		usleep_range(10000, 20000);
		timeout++;
	} while ((!link_up) && (timeout < 1000));

	if (timeout >= 1000) {
		netdev_warn(dev->net, "Timeout waiting for PHY link up\n");
		return -EIO;
	}

	/* phy reset */
	ret = smsc75xx_read_reg(dev, PMT_CTL, &buf);
	if (ret < 0) {
		netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret);
		return ret;
	}

	buf |= PMT_CTL_PHY_RST;

	ret = smsc75xx_write_reg(dev, PMT_CTL, buf);
	if (ret < 0) {
		netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret);
		return ret;
	}

	timeout = 0;
	do {
		usleep_range(10000, 20000);
		ret = smsc75xx_read_reg(dev, PMT_CTL, &buf);
		if (ret < 0) {
			netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n",
				    ret);
			return ret;
		}
		timeout++;
	} while ((buf & PMT_CTL_PHY_RST) && (timeout < 100));

	if (timeout >= 100) {
		netdev_warn(dev->net, "timeout waiting for PHY Reset\n");
		return -EIO;
	}

	return 0;
}

static int smsc75xx_reset(struct usbnet *dev)
{
	struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);