diff options
Diffstat (limited to 'drivers/net/phy/smsc.c')
| -rw-r--r-- | drivers/net/phy/smsc.c | 95 | 
1 files changed, 68 insertions, 27 deletions
| diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 88e3991464e7..11f34813e23f 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -43,7 +43,31 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev)  static int smsc_phy_config_init(struct phy_device *phydev)  { -	int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); +	int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); +	if (rc < 0) +		return rc; + +	/* If the SMSC PHY is in power down mode, then set it +	 * in all capable mode before using it. +	 */ +	if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { +		int timeout = 50000; + +		/* set "all capable" mode and reset the phy */ +		rc |= MII_LAN83C185_MODE_ALL; +		phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); +		phy_write(phydev, MII_BMCR, BMCR_RESET); + +		/* wait end of reset (max 500 ms) */ +		do { +			udelay(10); +			if (timeout-- == 0) +				return -1; +			rc = phy_read(phydev, MII_BMCR); +		} while (rc & BMCR_RESET); +	} + +	rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);  	if (rc < 0)  		return rc; @@ -56,35 +80,52 @@ static int smsc_phy_config_init(struct phy_device *phydev)  	return smsc_phy_ack_interrupt (phydev);  } -static int lan87xx_config_init(struct phy_device *phydev) +static int lan911x_config_init(struct phy_device *phydev)  { -	/* -	 * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on -	 * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due -	 * to a bug on the chip. -	 * -	 * When the system is powered on with the network cable being -	 * disconnected all the way until after ifconfig ethX up is -	 * issued for the LAN port with this PHY, connecting the cable -	 * afterwards does not cause LINK change detection, while the -	 * expected behavior is the Link UP being detected. -	 */ -	int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); -	if (rc < 0) -		return rc; - -	rc &= ~MII_LAN83C185_EDPWRDOWN; - -	rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc); -	if (rc < 0) -		return rc; -  	return smsc_phy_ack_interrupt(phydev);  } -static int lan911x_config_init(struct phy_device *phydev) +/* + * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each + * other in order to set the ENERGYON bit and exit EDPD mode.  If a link partner + * does send the pulses within this interval, the PHY will remained powered + * down. + * + * This workaround will manually toggle the PHY on/off upon calls to read_status + * in order to generate link test pulses if the link is down.  If a link partner + * is present, it will respond to the pulses, which will cause the ENERGYON bit + * to be set and will cause the EDPD mode to be exited. + */ +static int lan87xx_read_status(struct phy_device *phydev)  { -	return smsc_phy_ack_interrupt(phydev); +	int err = genphy_read_status(phydev); + +	if (!phydev->link) { +		/* Disable EDPD to wake up PHY */ +		int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); +		if (rc < 0) +			return rc; + +		rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, +			       rc & ~MII_LAN83C185_EDPWRDOWN); +		if (rc < 0) +			return rc; + +		/* Sleep 64 ms to allow ~5 link test pulses to be sent */ +		msleep(64); + +		/* Re-enable EDPD */ +		rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); +		if (rc < 0) +			return rc; + +		rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, +			       rc | MII_LAN83C185_EDPWRDOWN); +		if (rc < 0) +			return rc; +	} + +	return err;  }  static struct phy_driver smsc_phy_driver[] = { @@ -187,8 +228,8 @@ static struct phy_driver smsc_phy_driver[] = {  	/* basic functions */  	.config_aneg	= genphy_config_aneg, -	.read_status	= genphy_read_status, -	.config_init	= lan87xx_config_init, +	.read_status	= lan87xx_read_status, +	.config_init	= smsc_phy_config_init,  	/* IRQ related */  	.ack_interrupt	= smsc_phy_ack_interrupt, |