From a32d85b845bb7b34726e72853246738b4f5bd848 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lothar=20Wa=C3=9Fmann?= Date: Wed, 29 Oct 2014 08:38:29 +0100 Subject: [PATCH] phy: SMSC: fix autonegotiation --- drivers/net/phy/smsc.c | 101 +++++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index f64eb8fe04..23da3da711 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -11,31 +11,106 @@ * Copyright (c) 2006 Herbert Valerio Riedel */ #include +#include + +#define MII_LAN83C185_CTRL_STATUS 17 /* Mode/Status Register */ +#define MII_LAN83C185_EDPWRDOWN (1 << 13) /* EDPWRDOWN */ +#define MII_LAN83C185_ENERGYON (1 << 1) /* ENERGYON */ static int smsc_parse_status(struct phy_device *phydev) { - int mii_reg; + int bmcr; + int aneg_exp; + int mii_adv; + int lpa; + + aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION); + if (aneg_exp < 0) + return aneg_exp; + + if (aneg_exp & EXPANSION_MFAULTS) { + /* second read to clear latched status */ + aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION); + if (aneg_exp & EXPANSION_MFAULTS) + return -EIO; + } - mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + if (bmcr < 0) + return bmcr; + if (bmcr & BMCR_ANENABLE) { + lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); + if (lpa < 0) + return lpa; + mii_adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + if (mii_adv < 0) + return mii_adv; + lpa &= mii_adv; - if (mii_reg & (BMSR_100FULL | BMSR_100HALF)) - phydev->speed = SPEED_100; - else - phydev->speed = SPEED_10; + if (!(aneg_exp & EXPANSION_NWAY)) { + /* parallel detection */ + phydev->duplex = DUPLEX_HALF; + if (lpa & (LPA_100HALF | LPA_100FULL)) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + } - if (mii_reg & (BMSR_10FULL | BMSR_100FULL)) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; + if (lpa & (LPA_100FULL | LPA_100HALF)) { + phydev->speed = SPEED_100; + if (lpa & LPA_100FULL) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + } else if (lpa & (LPA_10FULL | LPA_10HALF)) { + phydev->speed = SPEED_10; + if (lpa & LPA_10FULL) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + } else { + return -EINVAL; + } + } else { + if (bmcr & BMCR_SPEED100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + } return 0; } static int smsc_startup(struct phy_device *phydev) { - genphy_update_link(phydev); - smsc_parse_status(phydev); - return 0; + int ret; + + if (!phydev->link) { + /* Disable EDPD to wake up PHY */ + ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS); + + if (ret < 0) + return ret; + + if (ret & MII_LAN83C185_EDPWRDOWN) { + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS, + ret & ~MII_LAN83C185_EDPWRDOWN); + if (ret < 0) + return ret; + + /* Sleep 64 ms to allow ~5 link test pulses to be sent */ + udelay(64 * 1000); + } + } + + ret = genphy_update_link(phydev); + if (ret < 0) + return ret; + return smsc_parse_status(phydev); } static struct phy_driver lan8700_driver = { -- 2.39.2