forked from mirrors/linux
		
	net: phy: fix resume handling
When a PHY has the BMCR_PDOWN bit set, it may decide to ignore writes to other registers, or reset the registers to power-on defaults. Micrel PHYs do this for their interrupt registers. The current structure of phylib tries to enable interrupts before resuming (and releasing) the BMCR_PDOWN bit. This fails, causing Micrel PHYs to stop working after a suspend/resume sequence if they are using interrupts. Fix this by ensuring that the PHY driver resume methods do not take the phydev->lock mutex themselves, but the callers of phy_resume() take that lock. This then allows us to move the call to phy_resume() before we enable interrupts in phy_start(). Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									cd8165c3d5
								
							
						
					
					
						commit
						f5e64032a7
					
				
					 3 changed files with 9 additions and 14 deletions
				
			
		|  | @ -239,14 +239,10 @@ static int at803x_resume(struct phy_device *phydev) | ||||||
| { | { | ||||||
| 	int value; | 	int value; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&phydev->lock); |  | ||||||
| 
 |  | ||||||
| 	value = phy_read(phydev, MII_BMCR); | 	value = phy_read(phydev, MII_BMCR); | ||||||
| 	value &= ~(BMCR_PDOWN | BMCR_ISOLATE); | 	value &= ~(BMCR_PDOWN | BMCR_ISOLATE); | ||||||
| 	phy_write(phydev, MII_BMCR, value); | 	phy_write(phydev, MII_BMCR, value); | ||||||
| 
 | 
 | ||||||
| 	mutex_unlock(&phydev->lock); |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -828,7 +828,6 @@ EXPORT_SYMBOL(phy_stop); | ||||||
|  */ |  */ | ||||||
| void phy_start(struct phy_device *phydev) | void phy_start(struct phy_device *phydev) | ||||||
| { | { | ||||||
| 	bool do_resume = false; |  | ||||||
| 	int err = 0; | 	int err = 0; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&phydev->lock); | 	mutex_lock(&phydev->lock); | ||||||
|  | @ -841,6 +840,9 @@ void phy_start(struct phy_device *phydev) | ||||||
| 		phydev->state = PHY_UP; | 		phydev->state = PHY_UP; | ||||||
| 		break; | 		break; | ||||||
| 	case PHY_HALTED: | 	case PHY_HALTED: | ||||||
|  | 		/* if phy was suspended, bring the physical link up again */ | ||||||
|  | 		phy_resume(phydev); | ||||||
|  | 
 | ||||||
| 		/* make sure interrupts are re-enabled for the PHY */ | 		/* make sure interrupts are re-enabled for the PHY */ | ||||||
| 		if (phydev->irq != PHY_POLL) { | 		if (phydev->irq != PHY_POLL) { | ||||||
| 			err = phy_enable_interrupts(phydev); | 			err = phy_enable_interrupts(phydev); | ||||||
|  | @ -849,17 +851,12 @@ void phy_start(struct phy_device *phydev) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		phydev->state = PHY_RESUMING; | 		phydev->state = PHY_RESUMING; | ||||||
| 		do_resume = true; |  | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	mutex_unlock(&phydev->lock); | 	mutex_unlock(&phydev->lock); | ||||||
| 
 | 
 | ||||||
| 	/* if phy was suspended, bring the physical link up again */ |  | ||||||
| 	if (do_resume) |  | ||||||
| 		phy_resume(phydev); |  | ||||||
| 
 |  | ||||||
| 	phy_trigger_machine(phydev, true); | 	phy_trigger_machine(phydev, true); | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(phy_start); | EXPORT_SYMBOL(phy_start); | ||||||
|  |  | ||||||
|  | @ -135,7 +135,9 @@ static int mdio_bus_phy_resume(struct device *dev) | ||||||
| 	if (!mdio_bus_phy_may_suspend(phydev)) | 	if (!mdio_bus_phy_may_suspend(phydev)) | ||||||
| 		goto no_resume; | 		goto no_resume; | ||||||
| 
 | 
 | ||||||
|  | 	mutex_lock(&phydev->lock); | ||||||
| 	ret = phy_resume(phydev); | 	ret = phy_resume(phydev); | ||||||
|  | 	mutex_unlock(&phydev->lock); | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		return ret; | 		return ret; | ||||||
| 
 | 
 | ||||||
|  | @ -1026,7 +1028,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | ||||||
| 	if (err) | 	if (err) | ||||||
| 		goto error; | 		goto error; | ||||||
| 
 | 
 | ||||||
|  | 	mutex_lock(&phydev->lock); | ||||||
| 	phy_resume(phydev); | 	phy_resume(phydev); | ||||||
|  | 	mutex_unlock(&phydev->lock); | ||||||
| 	phy_led_triggers_register(phydev); | 	phy_led_triggers_register(phydev); | ||||||
| 
 | 
 | ||||||
| 	return err; | 	return err; | ||||||
|  | @ -1157,6 +1161,8 @@ int phy_resume(struct phy_device *phydev) | ||||||
| 	struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); | 	struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
|  | 	WARN_ON(!mutex_is_locked(&phydev->lock)); | ||||||
|  | 
 | ||||||
| 	if (phydev->drv && phydrv->resume) | 	if (phydev->drv && phydrv->resume) | ||||||
| 		ret = phydrv->resume(phydev); | 		ret = phydrv->resume(phydev); | ||||||
| 
 | 
 | ||||||
|  | @ -1639,13 +1645,9 @@ int genphy_resume(struct phy_device *phydev) | ||||||
| { | { | ||||||
| 	int value; | 	int value; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&phydev->lock); |  | ||||||
| 
 |  | ||||||
| 	value = phy_read(phydev, MII_BMCR); | 	value = phy_read(phydev, MII_BMCR); | ||||||
| 	phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); | 	phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); | ||||||
| 
 | 
 | ||||||
| 	mutex_unlock(&phydev->lock); |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(genphy_resume); | EXPORT_SYMBOL(genphy_resume); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Russell King
						Russell King