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. Acked-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx> Signed-off-by: Lan Tianyu <tianyu.lan@xxxxxxxxx> --- drivers/usb/core/hub.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/usb/core/hub.h | 2 ++ drivers/usb/core/port.c | 11 +++++++++++ include/linux/usb.h | 1 + 4 files changed, 50 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8eeee62..1fff829 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -117,6 +117,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 diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index ddad018..c4bc6ba 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -98,6 +98,7 @@ struct usb_hub { * @child: usb device attatched to the port * @dev: generic device interface * @port_owner: port's owner + * @pm_qos: port's pm qos flags request * @connect_type: port's connect type * @portnum: port index num based one * @power_on: port's power state @@ -106,6 +107,7 @@ struct usb_port { struct usb_device *child; struct device dev; struct dev_state *port_owner; + struct dev_pm_qos_request pm_qos; enum usb_port_connect_type connect_type; u8 portnum; bool power_on; diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index f5842e6..0559b83 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -62,6 +62,10 @@ 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); usb_acpi_unregister_power_resources(dev); kfree(port_dev); @@ -146,6 +150,11 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) if (retval) goto error_register; + retval = dev_pm_qos_add_request(&port_dev->dev, &port_dev->pm_qos, + DEV_PM_QOS_FLAGS, 0); + if (retval) + goto error_add_request; + if (!dev_pm_qos_expose_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)) { pm_runtime_set_active(&port_dev->dev); @@ -156,6 +165,8 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) return 0; +error_add_request: + device_del(&port_dev->dev); error_register: put_device(&port_dev->dev); exit: diff --git a/include/linux/usb.h b/include/linux/usb.h index e996bb6..afaf920 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -585,6 +585,7 @@ extern struct usb_device *usb_get_dev(struct usb_device *dev); extern void usb_put_dev(struct usb_device *dev); extern struct usb_device *usb_hub_find_child(struct usb_device *hdev, int port1); +extern int usb_device_allow_power_off(struct usb_device *udev, bool set); /** * usb_hub_for_each_child - iterate over all child devices on the hub -- 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