Some usb devices can't be resumed correctly after power off. This patch is to add pm qos flags request to change NO_POWER_OFF and provide usb_device_allow_power_off() for device drivers to allow or prohibit usb core to power off the device. Signed-off-by: Lan Tianyu <tianyu.lan@xxxxxxxxx> --- drivers/usb/core/hub.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3ba6a96..fe05dff 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -49,6 +49,7 @@ struct usb_port { struct dev_state *port_owner; enum usb_port_connect_type connect_type; unsigned power_state:1; + struct dev_pm_qos_request pm_qos; }; struct usb_hub { @@ -203,6 +204,42 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev) return usb_get_intfdata(hdev->actconfig->interface[0]); } +/** + * usb_device_allow_power_off - Allow or prohibit power off device. + * @udev: target usb device + * @set: choice of allow or prohibit + * + * Clearing or setting usb port's pm qos flag PM_QOS_FLAG_NO_POWER_OFF + * to allow or prohibit target usb device to be power off. + */ +int usb_device_allow_power_off(struct usb_device *udev, bool set) +{ + struct usb_port *port_dev; + struct dev_pm_qos_request *pm_qos; + s32 value; + int ret; + + if (!udev->parent) + return -EINVAL; + + port_dev = + hdev_to_hub(udev->parent)->ports[udev->portnum - 1]; + pm_qos = &port_dev->pm_qos; + value = pm_qos->data.flr.flags; + + if (set) + value &= ~PM_QOS_FLAG_NO_POWER_OFF; + else + value |= PM_QOS_FLAG_NO_POWER_OFF; + + pm_runtime_get_sync(&port_dev->dev); + ret = dev_pm_qos_update_request(pm_qos, value); + pm_runtime_put(&port_dev->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_device_allow_power_off); + static int usb_device_supports_lpm(struct usb_device *udev) { /* USB 2.1 (and greater) devices indicate LPM support through @@ -1254,6 +1291,9 @@ static void usb_port_device_release(struct device *dev) { struct usb_port *port_dev = to_usb_port(dev); + pm_runtime_get_sync(dev); + dev_pm_qos_remove_request(&port_dev->pm_qos); + pm_runtime_put(dev); dev_pm_qos_hide_flags(dev); kfree(port_dev); } @@ -1344,8 +1384,17 @@ static int usb_hub_create_port_device(struct usb_hub *hub, if (retval) goto error_expose_pm_qos; + pm_runtime_get_sync(&port_dev->dev); + retval = dev_pm_qos_add_request(&port_dev->dev, &port_dev->pm_qos, + DEV_PM_QOS_FLAGS, 0); + if (retval) + goto error_add_qos_request; + + pm_runtime_put(&port_dev->dev); return 0; +error_add_qos_request: + pm_runtime_put(&port_dev->dev); error_expose_pm_qos: device_unregister(&port_dev->dev); error_register: -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html