Commit 971fcd492cebf544714f12d94549d2f0d2002645

Authored by Lan Tianyu
Committed by Greg Kroah-Hartman
1 parent 6802771bba

usb: add runtime pm support for usb port device

This patch is to add runtime pm callback for usb port device.
Set/clear PORT_POWER feature in the resume/suspend callback.
Add portnum for struct usb_port to record port number. Do
pm_rumtime_get_sync/put(portdev) when a device is plugged/unplugged
to prevent it from being powered off when it is active.

Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 3 changed files with 77 additions and 0 deletions Side-by-side Diff

drivers/usb/core/hub.c
... ... @@ -715,6 +715,27 @@
715 715 }
716 716  
717 717 /**
  718 + * usb_hub_set_port_power - control hub port's power state
  719 + * @hdev: target hub
  720 + * @port1: port index
  721 + * @set: expected status
  722 + *
  723 + * call this function to control port's power via setting or
  724 + * clearing the port's PORT_POWER feature.
  725 + */
  726 +int usb_hub_set_port_power(struct usb_device *hdev, int port1,
  727 + bool set)
  728 +{
  729 + int ret;
  730 +
  731 + if (set)
  732 + ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
  733 + else
  734 + ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
  735 + return ret;
  736 +}
  737 +
  738 +/**
718 739 * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub
719 740 * @urb: an URB associated with the failed or incomplete split transaction
720 741 *
... ... @@ -1569,6 +1590,7 @@
1569 1590 kfree(hub->status);
1570 1591 kfree(hub->buffer);
1571 1592  
  1593 + pm_suspend_ignore_children(&intf->dev, false);
1572 1594 kref_put(&hub->kref, hub_release);
1573 1595 }
1574 1596  
... ... @@ -1671,6 +1693,7 @@
1671 1693  
1672 1694 usb_set_intfdata (intf, hub);
1673 1695 intf->needs_remote_wakeup = 1;
  1696 + pm_suspend_ignore_children(&intf->dev, true);
1674 1697  
1675 1698 if (hdev->speed == USB_SPEED_HIGH)
1676 1699 highspeed_hubs++;
... ... @@ -1997,6 +2020,8 @@
1997 2020  
1998 2021 sysfs_remove_link(&udev->dev.kobj, "port");
1999 2022 sysfs_remove_link(&port_dev->dev.kobj, "device");
  2023 +
  2024 + pm_runtime_put(&port_dev->dev);
2000 2025 }
2001 2026  
2002 2027 usb_remove_ep_devs(&udev->ep0);
... ... @@ -2307,6 +2332,8 @@
2307 2332 sysfs_remove_link(&udev->dev.kobj, "port");
2308 2333 goto fail;
2309 2334 }
  2335 +
  2336 + pm_runtime_get_sync(&port_dev->dev);
2310 2337 }
2311 2338  
2312 2339 (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
drivers/usb/core/hub.h
... ... @@ -79,12 +79,14 @@
79 79 * @dev: generic device interface
80 80 * @port_owner: port's owner
81 81 * @connect_type: port's connect type
  82 + * @portnum: port index num based one
82 83 */
83 84 struct usb_port {
84 85 struct usb_device *child;
85 86 struct device dev;
86 87 struct dev_state *port_owner;
87 88 enum usb_port_connect_type connect_type;
  89 + u8 portnum;
88 90 };
89 91  
90 92 #define to_usb_port(_dev) \
... ... @@ -94,4 +96,6 @@
94 96 int port1);
95 97 extern void usb_hub_remove_port_device(struct usb_hub *hub,
96 98 int port1);
  99 +extern int usb_hub_set_port_power(struct usb_device *hdev,
  100 + int port1, bool set);
drivers/usb/core/port.c
... ... @@ -17,6 +17,7 @@
17 17 */
18 18  
19 19 #include <linux/slab.h>
  20 +#include <linux/pm_qos.h>
20 21  
21 22 #include "hub.h"
22 23  
23 24  
... ... @@ -70,9 +71,50 @@
70 71 kfree(port_dev);
71 72 }
72 73  
  74 +#ifdef CONFIG_USB_SUSPEND
  75 +static int usb_port_runtime_resume(struct device *dev)
  76 +{
  77 + struct usb_port *port_dev = to_usb_port(dev);
  78 + struct usb_device *hdev = to_usb_device(dev->parent->parent);
  79 + struct usb_interface *intf = to_usb_interface(dev->parent);
  80 + int retval;
  81 +
  82 + usb_autopm_get_interface(intf);
  83 + retval = usb_hub_set_port_power(hdev, port_dev->portnum, true);
  84 + usb_autopm_put_interface(intf);
  85 + return retval;
  86 +}
  87 +
  88 +static int usb_port_runtime_suspend(struct device *dev)
  89 +{
  90 + struct usb_port *port_dev = to_usb_port(dev);
  91 + struct usb_device *hdev = to_usb_device(dev->parent->parent);
  92 + struct usb_interface *intf = to_usb_interface(dev->parent);
  93 + int retval;
  94 +
  95 + if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
  96 + == PM_QOS_FLAGS_ALL)
  97 + return -EAGAIN;
  98 +
  99 + usb_autopm_get_interface(intf);
  100 + retval = usb_hub_set_port_power(hdev, port_dev->portnum, false);
  101 + usb_autopm_put_interface(intf);
  102 + return retval;
  103 +}
  104 +#endif
  105 +
  106 +static const struct dev_pm_ops usb_port_pm_ops = {
  107 +#ifdef CONFIG_USB_SUSPEND
  108 + .runtime_suspend = usb_port_runtime_suspend,
  109 + .runtime_resume = usb_port_runtime_resume,
  110 + .runtime_idle = pm_generic_runtime_idle,
  111 +#endif
  112 +};
  113 +
73 114 struct device_type usb_port_device_type = {
74 115 .name = "usb_port",
75 116 .release = usb_port_device_release,
  117 + .pm = &usb_port_pm_ops,
76 118 };
77 119  
78 120 int usb_hub_create_port_device(struct usb_hub *hub, int port1)
... ... @@ -87,6 +129,7 @@
87 129 }
88 130  
89 131 hub->ports[port1 - 1] = port_dev;
  132 + port_dev->portnum = port1;
90 133 port_dev->dev.parent = hub->intfdev;
91 134 port_dev->dev.groups = port_dev_group;
92 135 port_dev->dev.type = &usb_port_device_type;
... ... @@ -95,6 +138,9 @@
95 138 retval = device_register(&port_dev->dev);
96 139 if (retval)
97 140 goto error_register;
  141 +
  142 + pm_runtime_set_active(&port_dev->dev);
  143 + pm_runtime_enable(&port_dev->dev);
98 144  
99 145 retval = usb_acpi_register_power_resources(&port_dev->dev);
100 146 if (retval && retval != -ENODEV)