diff options
Diffstat (limited to 'drivers/net/usb')
| -rw-r--r-- | drivers/net/usb/asix_devices.c | 29 | ||||
| -rw-r--r-- | drivers/net/usb/lan78xx.c | 11 | 
2 files changed, 36 insertions, 4 deletions
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 792ddda1ad49..85bd5d845409 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -625,6 +625,21 @@ static void ax88772_suspend(struct usbnet *dev)  		   asix_read_medium_status(dev, 1));  } +/* Notes on PM callbacks and locking context: + * + * - asix_suspend()/asix_resume() are invoked for both runtime PM and + *   system-wide suspend/resume. For struct usb_driver the ->resume() + *   callback does not receive pm_message_t, so the resume type cannot + *   be distinguished here. + * + * - The MAC driver must hold RTNL when calling phylink interfaces such as + *   phylink_suspend()/resume(). Those calls will also perform MDIO I/O. + * + * - Taking RTNL and doing MDIO from a runtime-PM resume callback (while + *   the USB PM lock is held) is fragile. Since autosuspend brings no + *   measurable power saving here, we block it by holding a PM usage + *   reference in ax88772_bind(). + */  static int asix_suspend(struct usb_interface *intf, pm_message_t message)  {  	struct usbnet *dev = usb_get_intfdata(intf); @@ -919,6 +934,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)  	if (ret)  		goto initphy_err; +	/* Keep this interface runtime-PM active by taking a usage ref. +	 * Prevents runtime suspend while bound and avoids resume paths +	 * that could deadlock (autoresume under RTNL while USB PM lock +	 * is held, phylink/MDIO wants RTNL). +	 */ +	pm_runtime_get_noresume(&intf->dev); +  	return 0;  initphy_err: @@ -948,6 +970,8 @@ static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)  	phylink_destroy(priv->phylink);  	ax88772_mdio_unregister(priv);  	asix_rx_fixup_common_free(dev->driver_priv); +	/* Drop the PM usage ref taken in bind() */ +	pm_runtime_put(&intf->dev);  }  static void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf) @@ -1600,6 +1624,11 @@ static struct usb_driver asix_driver = {  	.resume =	asix_resume,  	.reset_resume =	asix_resume,  	.disconnect =	usbnet_disconnect, +	/* usbnet enables autosuspend by default (supports_autosuspend=1). +	 * We keep runtime-PM active for AX88772* by taking a PM usage +	 * reference in ax88772_bind() (pm_runtime_get_noresume()) and +	 * dropping it in unbind(), which effectively blocks autosuspend. +	 */  	.supports_autosuspend = 1,  	.disable_hub_initiated_lpm = 1,  }; diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index b56e2459ee3c..42d35cc6b421 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1080,10 +1080,13 @@ static int lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset,  	}  read_raw_eeprom_done: -	if (dev->chipid == ID_REV_CHIP_ID_7800_) -		return lan78xx_write_reg(dev, HW_CFG, saved); - -	return 0; +	if (dev->chipid == ID_REV_CHIP_ID_7800_) { +		int rc = lan78xx_write_reg(dev, HW_CFG, saved); +		/* If USB fails, there is nothing to do */ +		if (rc < 0) +			return rc; +	} +	return ret;  }  static int lan78xx_read_eeprom(struct lan78xx_net *dev, u32 offset,  | 
