Commit 0a8d3f2e authored by Tristram Ha's avatar Tristram Ha Committed by David S. Miller

net: phy: Micrel KSZ8061: fix errata solution not taking effect problem

KSZ8061 needs to write to a MMD register at driver initialization to fix
an errata.  This worked in 5.0 kernel but not in newer kernels.  The
issue is the main phylib code no longer resets PHY at the very beginning.
Calling phy resuming code later will reset the chip if it is already
powered down at the beginning.  This wipes out the MMD register write.
Solution is to implement a phy resume function for KSZ8061 to take care
of this problem.

Fixes: 232ba3a5 ("net: phy: Micrel KSZ8061: link failure after cable connect")
Signed-off-by: default avatarTristram Ha <tristram.ha@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fb0aa078
...@@ -866,6 +866,17 @@ static int ksz8061_config_init(struct phy_device *phydev) ...@@ -866,6 +866,17 @@ static int ksz8061_config_init(struct phy_device *phydev)
{ {
int ret; int ret;
/* Chip can be powered down by the bootstrap code. */
ret = phy_read(phydev, MII_BMCR);
if (ret < 0)
return ret;
if (ret & BMCR_PDOWN) {
ret = phy_write(phydev, MII_BMCR, ret & ~BMCR_PDOWN);
if (ret < 0)
return ret;
usleep_range(1000, 2000);
}
ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A);
if (ret) if (ret)
return ret; return ret;
...@@ -2135,6 +2146,35 @@ static int ksz9477_resume(struct phy_device *phydev) ...@@ -2135,6 +2146,35 @@ static int ksz9477_resume(struct phy_device *phydev)
return 0; return 0;
} }
static int ksz8061_resume(struct phy_device *phydev)
{
int ret;
/* This function can be called twice when the Ethernet device is on. */
ret = phy_read(phydev, MII_BMCR);
if (ret < 0)
return ret;
if (!(ret & BMCR_PDOWN))
return 0;
genphy_resume(phydev);
usleep_range(1000, 2000);
/* Re-program the value after chip is reset. */
ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A);
if (ret)
return ret;
/* Enable PHY Interrupts */
if (phy_interrupt_is_valid(phydev)) {
phydev->interrupts = PHY_INTERRUPT_ENABLED;
if (phydev->drv->config_intr)
phydev->drv->config_intr(phydev);
}
return 0;
}
static int kszphy_probe(struct phy_device *phydev) static int kszphy_probe(struct phy_device *phydev)
{ {
const struct kszphy_type *type = phydev->drv->driver_data; const struct kszphy_type *type = phydev->drv->driver_data;
...@@ -5389,7 +5429,7 @@ static struct phy_driver ksphy_driver[] = { ...@@ -5389,7 +5429,7 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = kszphy_config_intr, .config_intr = kszphy_config_intr,
.handle_interrupt = kszphy_handle_interrupt, .handle_interrupt = kszphy_handle_interrupt,
.suspend = kszphy_suspend, .suspend = kszphy_suspend,
.resume = kszphy_resume, .resume = ksz8061_resume,
}, { }, {
.phy_id = PHY_ID_KSZ9021, .phy_id = PHY_ID_KSZ9021,
.phy_id_mask = 0x000ffffe, .phy_id_mask = 0x000ffffe,
......
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