Commit ad493e5e580546e6c3024b76a41535476da1546a

Authored by Lan Tianyu
Committed by Greg Kroah-Hartman
1 parent 971fcd492c

usb: add usb port auto power off mechanism

This patch is to add usb port auto power off mechanism.
When usb device is suspending, usb core will suspend usb port and
usb port runtime pm callback will clear PORT_POWER feature to
power off port if all conditions were met. These conditions are
remote wakeup disable, pm qos NO_POWER_OFF flag clear and persist
enable. When it resumes, power on port again.

Add did_runtime_put in the struct usb_port to ensure
pm_runtime_get/put(portdev) to be called pairedly.  Set did_runtime_put
to true when call pm_runtime_put(portdev) during suspending. The
pm_runtime_get(portdev) only will be called when did_runtime_put
is set to true during resuming. Set did_runtime_put to false after
calling pm_runtime_get(portdev).

Make clear_port_feature() and hdev_to_hub() as global symbol.
Rename clear_port_feature() to usb_clear_port_feature() and
hdev_to_hub() to usb_hub_to_struct_hub().

Extend hub_port_debounce() with the fuction of debouncing to
be connected. Add two wraps: hub_port_debounce_be_connected()
and hub_port_debouce_be_stable().

Increase HUB_DEBOUNCE_TIMEOUT to 2000 because some usb ssds
needs around 1.5 or more to make the hub port status to be
connected steadily after being powered off and powered on.

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 164 additions and 58 deletions Side-by-side Diff

drivers/usb/core/hub.c
... ... @@ -26,6 +26,7 @@
26 26 #include <linux/mutex.h>
27 27 #include <linux/freezer.h>
28 28 #include <linux/random.h>
  29 +#include <linux/pm_qos.h>
29 30  
30 31 #include <asm/uaccess.h>
31 32 #include <asm/byteorder.h>
... ... @@ -108,7 +109,7 @@
108 109 DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
109 110 EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
110 111  
111   -#define HUB_DEBOUNCE_TIMEOUT 1500
  112 +#define HUB_DEBOUNCE_TIMEOUT 2000
112 113 #define HUB_DEBOUNCE_STEP 25
113 114 #define HUB_DEBOUNCE_STABLE 100
114 115  
... ... @@ -127,7 +128,7 @@
127 128 }
128 129  
129 130 /* Note that hdev or one of its children must be locked! */
130   -static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
  131 +struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
131 132 {
132 133 if (!hdev || !hdev->actconfig || !hdev->maxchild)
133 134 return NULL;
... ... @@ -301,7 +302,7 @@
301 302 if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER)
302 303 return;
303 304  
304   - hub = hdev_to_hub(udev->parent);
  305 + hub = usb_hub_to_struct_hub(udev->parent);
305 306 /* It doesn't take time to transition the roothub into U0, since it
306 307 * doesn't have an upstream link.
307 308 */
... ... @@ -393,7 +394,7 @@
393 394 /*
394 395 * USB 2.0 spec Section 11.24.2.2
395 396 */
396   -static int clear_port_feature(struct usb_device *hdev, int port1, int feature)
  397 +int usb_clear_port_feature(struct usb_device *hdev, int port1, int feature)
397 398 {
398 399 return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
399 400 USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
... ... @@ -586,7 +587,7 @@
586 587  
587 588 void usb_kick_khubd(struct usb_device *hdev)
588 589 {
589   - struct usb_hub *hub = hdev_to_hub(hdev);
  590 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
590 591  
591 592 if (hub)
592 593 kick_khubd(hub);
... ... @@ -608,7 +609,7 @@
608 609 if (!hdev)
609 610 return;
610 611  
611   - hub = hdev_to_hub(hdev);
  612 + hub = usb_hub_to_struct_hub(hdev);
612 613 if (hub) {
613 614 set_bit(portnum, hub->wakeup_bits);
614 615 kick_khubd(hub);
615 616  
... ... @@ -727,11 +728,16 @@
727 728 bool set)
728 729 {
729 730 int ret;
  731 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
  732 + struct usb_port *port_dev = hub->ports[port1 - 1];
730 733  
731 734 if (set)
732 735 ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
733 736 else
734   - ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
  737 + ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
  738 +
  739 + if (!ret)
  740 + port_dev->power_is_on = set;
735 741 return ret;
736 742 }
737 743  
... ... @@ -811,7 +817,11 @@
811 817 dev_dbg(hub->intfdev, "trying to enable port power on "
812 818 "non-switchable hub\n");
813 819 for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
814   - set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
  820 + if (hub->ports[port1 - 1]->power_is_on)
  821 + set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
  822 + else
  823 + usb_clear_port_feature(hub->hdev, port1,
  824 + USB_PORT_FEAT_POWER);
815 825  
816 826 /* Wait at least 100 msec for power to become stable */
817 827 delay = max(pgood_delay, (unsigned) 100);
... ... @@ -905,7 +915,7 @@
905 915 if (hub_is_superspeed(hub->hdev))
906 916 ret = hub_usb3_port_disable(hub, port1);
907 917 else
908   - ret = clear_port_feature(hdev, port1,
  918 + ret = usb_clear_port_feature(hdev, port1,
909 919 USB_PORT_FEAT_ENABLE);
910 920 }
911 921 if (ret)
... ... @@ -954,7 +964,7 @@
954 964  
955 965 if (!udev->parent) /* Can't remove a root hub */
956 966 return -EINVAL;
957   - hub = hdev_to_hub(udev->parent);
  967 + hub = usb_hub_to_struct_hub(udev->parent);
958 968 intf = to_usb_interface(hub->intfdev);
959 969  
960 970 usb_autopm_get_interface(intf);
... ... @@ -1086,7 +1096,7 @@
1086 1096 * Do not disable USB3 protocol ports.
1087 1097 */
1088 1098 if (!hub_is_superspeed(hdev)) {
1089   - clear_port_feature(hdev, port1,
  1099 + usb_clear_port_feature(hdev, port1,
1090 1100 USB_PORT_FEAT_ENABLE);
1091 1101 portstatus &= ~USB_PORT_STAT_ENABLE;
1092 1102 } else {
1093 1103  
1094 1104  
... ... @@ -1098,18 +1108,18 @@
1098 1108 /* Clear status-change flags; we'll debounce later */
1099 1109 if (portchange & USB_PORT_STAT_C_CONNECTION) {
1100 1110 need_debounce_delay = true;
1101   - clear_port_feature(hub->hdev, port1,
  1111 + usb_clear_port_feature(hub->hdev, port1,
1102 1112 USB_PORT_FEAT_C_CONNECTION);
1103 1113 }
1104 1114 if (portchange & USB_PORT_STAT_C_ENABLE) {
1105 1115 need_debounce_delay = true;
1106   - clear_port_feature(hub->hdev, port1,
  1116 + usb_clear_port_feature(hub->hdev, port1,
1107 1117 USB_PORT_FEAT_C_ENABLE);
1108 1118 }
1109 1119 if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
1110 1120 hub_is_superspeed(hub->hdev)) {
1111 1121 need_debounce_delay = true;
1112   - clear_port_feature(hub->hdev, port1,
  1122 + usb_clear_port_feature(hub->hdev, port1,
1113 1123 USB_PORT_FEAT_C_BH_PORT_RESET);
1114 1124 }
1115 1125 /* We can forget about a "removed" device when there's a
1116 1126  
... ... @@ -1143,10 +1153,16 @@
1143 1153 set_bit(port1, hub->change_bits);
1144 1154  
1145 1155 } else if (udev->persist_enabled) {
  1156 + struct usb_port *port_dev = hub->ports[port1 - 1];
  1157 +
1146 1158 #ifdef CONFIG_PM
1147 1159 udev->reset_resume = 1;
1148 1160 #endif
1149   - set_bit(port1, hub->change_bits);
  1161 + /* Don't set the change_bits when the device
  1162 + * was powered off.
  1163 + */
  1164 + if (port_dev->power_is_on)
  1165 + set_bit(port1, hub->change_bits);
1150 1166  
1151 1167 } else {
1152 1168 /* The power session is gone; tell khubd */
... ... @@ -1712,7 +1728,7 @@
1712 1728 hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
1713 1729 {
1714 1730 struct usb_device *hdev = interface_to_usbdev (intf);
1715   - struct usb_hub *hub = hdev_to_hub(hdev);
  1731 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
1716 1732  
1717 1733 /* assert ifno == 0 (part of hub spec) */
1718 1734 switch (code) {
... ... @@ -1758,7 +1774,7 @@
1758 1774 /* This assumes that devices not managed by the hub driver
1759 1775 * will always have maxchild equal to 0.
1760 1776 */
1761   - *ppowner = &(hdev_to_hub(hdev)->ports[port1 - 1]->port_owner);
  1777 + *ppowner = &(usb_hub_to_struct_hub(hdev)->ports[port1 - 1]->port_owner);
1762 1778 return 0;
1763 1779 }
1764 1780  
... ... @@ -1795,7 +1811,7 @@
1795 1811  
1796 1812 void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
1797 1813 {
1798   - struct usb_hub *hub = hdev_to_hub(hdev);
  1814 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
1799 1815 int n;
1800 1816  
1801 1817 for (n = 0; n < hdev->maxchild; n++) {
1802 1818  
... ... @@ -1812,13 +1828,13 @@
1812 1828  
1813 1829 if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
1814 1830 return false;
1815   - hub = hdev_to_hub(udev->parent);
  1831 + hub = usb_hub_to_struct_hub(udev->parent);
1816 1832 return !!hub->ports[udev->portnum - 1]->port_owner;
1817 1833 }
1818 1834  
1819 1835 static void recursively_mark_NOTATTACHED(struct usb_device *udev)
1820 1836 {
1821   - struct usb_hub *hub = hdev_to_hub(udev);
  1837 + struct usb_hub *hub = usb_hub_to_struct_hub(udev);
1822 1838 int i;
1823 1839  
1824 1840 for (i = 0; i < udev->maxchild; ++i) {
... ... @@ -1987,7 +2003,7 @@
1987 2003 void usb_disconnect(struct usb_device **pdev)
1988 2004 {
1989 2005 struct usb_device *udev = *pdev;
1990   - struct usb_hub *hub = hdev_to_hub(udev);
  2006 + struct usb_hub *hub = usb_hub_to_struct_hub(udev);
1991 2007 int i;
1992 2008  
1993 2009 /* mark the device as inactive, so any further urb submissions for
1994 2010  
... ... @@ -2015,13 +2031,16 @@
2015 2031 usb_hcd_synchronize_unlinks(udev);
2016 2032  
2017 2033 if (udev->parent) {
2018   - struct usb_port *port_dev =
2019   - hdev_to_hub(udev->parent)->ports[udev->portnum - 1];
  2034 + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
  2035 + struct usb_port *port_dev = hub->ports[udev->portnum - 1];
2020 2036  
2021 2037 sysfs_remove_link(&udev->dev.kobj, "port");
2022 2038 sysfs_remove_link(&port_dev->dev.kobj, "device");
2023 2039  
2024   - pm_runtime_put(&port_dev->dev);
  2040 + if (!port_dev->did_runtime_put)
  2041 + pm_runtime_put(&port_dev->dev);
  2042 + else
  2043 + port_dev->did_runtime_put = false;
2025 2044 }
2026 2045  
2027 2046 usb_remove_ep_devs(&udev->ep0);
... ... @@ -2210,7 +2229,7 @@
2210 2229 if (!hdev)
2211 2230 return;
2212 2231  
2213   - hub = hdev_to_hub(udev->parent);
  2232 + hub = usb_hub_to_struct_hub(udev->parent);
2214 2233  
2215 2234 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
2216 2235  
... ... @@ -2318,8 +2337,8 @@
2318 2337  
2319 2338 /* Create link files between child device and usb port device. */
2320 2339 if (udev->parent) {
2321   - struct usb_port *port_dev =
2322   - hdev_to_hub(udev->parent)->ports[udev->portnum - 1];
  2340 + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
  2341 + struct usb_port *port_dev = hub->ports[udev->portnum - 1];
2323 2342  
2324 2343 err = sysfs_create_link(&udev->dev.kobj,
2325 2344 &port_dev->dev.kobj, "port");
2326 2345  
2327 2346  
2328 2347  
... ... @@ -2567,14 +2586,14 @@
2567 2586 /* FALL THROUGH */
2568 2587 case -ENOTCONN:
2569 2588 case -ENODEV:
2570   - clear_port_feature(hub->hdev,
  2589 + usb_clear_port_feature(hub->hdev,
2571 2590 port1, USB_PORT_FEAT_C_RESET);
2572 2591 if (hub_is_superspeed(hub->hdev)) {
2573   - clear_port_feature(hub->hdev, port1,
  2592 + usb_clear_port_feature(hub->hdev, port1,
2574 2593 USB_PORT_FEAT_C_BH_PORT_RESET);
2575   - clear_port_feature(hub->hdev, port1,
  2594 + usb_clear_port_feature(hub->hdev, port1,
2576 2595 USB_PORT_FEAT_C_PORT_LINK_STATE);
2577   - clear_port_feature(hub->hdev, port1,
  2596 + usb_clear_port_feature(hub->hdev, port1,
2578 2597 USB_PORT_FEAT_C_CONNECTION);
2579 2598 }
2580 2599 if (udev)
2581 2600  
... ... @@ -2748,10 +2767,10 @@
2748 2767  
2749 2768 /* Late port handoff can set status-change bits */
2750 2769 if (portchange & USB_PORT_STAT_C_CONNECTION)
2751   - clear_port_feature(hub->hdev, port1,
  2770 + usb_clear_port_feature(hub->hdev, port1,
2752 2771 USB_PORT_FEAT_C_CONNECTION);
2753 2772 if (portchange & USB_PORT_STAT_C_ENABLE)
2754   - clear_port_feature(hub->hdev, port1,
  2773 + usb_clear_port_feature(hub->hdev, port1,
2755 2774 USB_PORT_FEAT_C_ENABLE);
2756 2775 }
2757 2776  
... ... @@ -2852,7 +2871,9 @@
2852 2871 */
2853 2872 int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2854 2873 {
2855   - struct usb_hub *hub = hdev_to_hub(udev->parent);
  2874 + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
  2875 + struct usb_port *port_dev = hub->ports[udev->portnum - 1];
  2876 + enum pm_qos_flags_status pm_qos_stat;
2856 2877 int port1 = udev->portnum;
2857 2878 int status;
2858 2879  
... ... @@ -2945,6 +2966,21 @@
2945 2966 udev->port_is_suspended = 1;
2946 2967 msleep(10);
2947 2968 }
  2969 +
  2970 + /*
  2971 + * Check whether current status meets the requirement of
  2972 + * usb port power off mechanism
  2973 + */
  2974 + pm_qos_stat = dev_pm_qos_flags(&port_dev->dev,
  2975 + PM_QOS_FLAG_NO_POWER_OFF);
  2976 + if (!udev->do_remote_wakeup
  2977 + && pm_qos_stat != PM_QOS_FLAGS_ALL
  2978 + && udev->persist_enabled
  2979 + && !status) {
  2980 + pm_runtime_put_sync(&port_dev->dev);
  2981 + port_dev->did_runtime_put = true;
  2982 + }
  2983 +
2948 2984 usb_mark_last_busy(hub->hdev);
2949 2985 return status;
2950 2986 }
2951 2987  
... ... @@ -3070,11 +3106,22 @@
3070 3106 */
3071 3107 int usb_port_resume(struct usb_device *udev, pm_message_t msg)
3072 3108 {
3073   - struct usb_hub *hub = hdev_to_hub(udev->parent);
  3109 + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
  3110 + struct usb_port *port_dev = hub->ports[udev->portnum - 1];
3074 3111 int port1 = udev->portnum;
3075 3112 int status;
3076 3113 u16 portchange, portstatus;
3077 3114  
  3115 + if (port_dev->did_runtime_put) {
  3116 + status = pm_runtime_get_sync(&port_dev->dev);
  3117 + port_dev->did_runtime_put = false;
  3118 + if (status < 0) {
  3119 + dev_dbg(&udev->dev, "can't resume usb port, status %d\n",
  3120 + status);
  3121 + return status;
  3122 + }
  3123 + }
  3124 +
3078 3125 /* Skip the initial Clear-Suspend step for a remote wakeup */
3079 3126 status = hub_port_status(hub, port1, &portstatus, &portchange);
3080 3127 if (status == 0 && !port_is_suspended(hub, portstatus))
... ... @@ -3088,7 +3135,7 @@
3088 3135 if (hub_is_superspeed(hub->hdev))
3089 3136 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
3090 3137 else
3091   - status = clear_port_feature(hub->hdev,
  3138 + status = usb_clear_port_feature(hub->hdev,
3092 3139 port1, USB_PORT_FEAT_SUSPEND);
3093 3140 if (status) {
3094 3141 dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
3095 3142  
... ... @@ -3114,11 +3161,11 @@
3114 3161 udev->port_is_suspended = 0;
3115 3162 if (hub_is_superspeed(hub->hdev)) {
3116 3163 if (portchange & USB_PORT_STAT_C_LINK_STATE)
3117   - clear_port_feature(hub->hdev, port1,
  3164 + usb_clear_port_feature(hub->hdev, port1,
3118 3165 USB_PORT_FEAT_C_PORT_LINK_STATE);
3119 3166 } else {
3120 3167 if (portchange & USB_PORT_STAT_C_SUSPEND)
3121   - clear_port_feature(hub->hdev, port1,
  3168 + usb_clear_port_feature(hub->hdev, port1,
3122 3169 USB_PORT_FEAT_C_SUSPEND);
3123 3170 }
3124 3171 }
... ... @@ -3174,7 +3221,7 @@
3174 3221  
3175 3222 int usb_port_resume(struct usb_device *udev, pm_message_t msg)
3176 3223 {
3177   - struct usb_hub *hub = hdev_to_hub(udev->parent);
  3224 + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
3178 3225 int port1 = udev->portnum;
3179 3226 int status;
3180 3227 u16 portchange, portstatus;
... ... @@ -3753,7 +3800,7 @@
3753 3800 * every 25ms for transient disconnects. When the port status has been
3754 3801 * unchanged for 100ms it returns the port status.
3755 3802 */
3756   -static int hub_port_debounce(struct usb_hub *hub, int port1)
  3803 +int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
3757 3804 {
3758 3805 int ret;
3759 3806 int total_time, stable_time = 0;
... ... @@ -3767,7 +3814,9 @@
3767 3814  
3768 3815 if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&
3769 3816 (portstatus & USB_PORT_STAT_CONNECTION) == connection) {
3770   - stable_time += HUB_DEBOUNCE_STEP;
  3817 + if (!must_be_connected ||
  3818 + (connection == USB_PORT_STAT_CONNECTION))
  3819 + stable_time += HUB_DEBOUNCE_STEP;
3771 3820 if (stable_time >= HUB_DEBOUNCE_STABLE)
3772 3821 break;
3773 3822 } else {
... ... @@ -3776,7 +3825,7 @@
3776 3825 }
3777 3826  
3778 3827 if (portchange & USB_PORT_STAT_C_CONNECTION) {
3779   - clear_port_feature(hub->hdev, port1,
  3828 + usb_clear_port_feature(hub->hdev, port1,
3780 3829 USB_PORT_FEAT_C_CONNECTION);
3781 3830 }
3782 3831  
... ... @@ -4288,7 +4337,7 @@
4288 4337  
4289 4338 if (portchange & (USB_PORT_STAT_C_CONNECTION |
4290 4339 USB_PORT_STAT_C_ENABLE)) {
4291   - status = hub_port_debounce(hub, port1);
  4340 + status = hub_port_debounce_be_stable(hub, port1);
4292 4341 if (status < 0) {
4293 4342 if (printk_ratelimit())
4294 4343 dev_err(hub_dev, "connect-debounce failed, "
... ... @@ -4467,7 +4516,7 @@
4467 4516 if (!hub_is_superspeed(hdev)) {
4468 4517 if (!(portchange & USB_PORT_STAT_C_SUSPEND))
4469 4518 return 0;
4470   - clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
  4519 + usb_clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
4471 4520 } else {
4472 4521 if (!udev || udev->state != USB_STATE_SUSPENDED ||
4473 4522 (portstatus & USB_PORT_STAT_LINK_STATE) !=
... ... @@ -4595,7 +4644,7 @@
4595 4644 continue;
4596 4645  
4597 4646 if (portchange & USB_PORT_STAT_C_CONNECTION) {
4598   - clear_port_feature(hdev, i,
  4647 + usb_clear_port_feature(hdev, i,
4599 4648 USB_PORT_FEAT_C_CONNECTION);
4600 4649 connect_change = 1;
4601 4650 }
... ... @@ -4606,7 +4655,7 @@
4606 4655 "port %d enable change, "
4607 4656 "status %08x\n",
4608 4657 i, portstatus);
4609   - clear_port_feature(hdev, i,
  4658 + usb_clear_port_feature(hdev, i,
4610 4659 USB_PORT_FEAT_C_ENABLE);
4611 4660  
4612 4661 /*
... ... @@ -4637,7 +4686,7 @@
4637 4686  
4638 4687 dev_dbg(hub_dev, "over-current change on port "
4639 4688 "%d\n", i);
4640   - clear_port_feature(hdev, i,
  4689 + usb_clear_port_feature(hdev, i,
4641 4690 USB_PORT_FEAT_C_OVER_CURRENT);
4642 4691 msleep(100); /* Cool down */
4643 4692 hub_power_on(hub, true);
... ... @@ -4651,7 +4700,7 @@
4651 4700 dev_dbg (hub_dev,
4652 4701 "reset change on port %d\n",
4653 4702 i);
4654   - clear_port_feature(hdev, i,
  4703 + usb_clear_port_feature(hdev, i,
4655 4704 USB_PORT_FEAT_C_RESET);
4656 4705 }
4657 4706 if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
4658 4707  
4659 4708  
... ... @@ -4659,18 +4708,18 @@
4659 4708 dev_dbg(hub_dev,
4660 4709 "warm reset change on port %d\n",
4661 4710 i);
4662   - clear_port_feature(hdev, i,
  4711 + usb_clear_port_feature(hdev, i,
4663 4712 USB_PORT_FEAT_C_BH_PORT_RESET);
4664 4713 }
4665 4714 if (portchange & USB_PORT_STAT_C_LINK_STATE) {
4666   - clear_port_feature(hub->hdev, i,
  4715 + usb_clear_port_feature(hub->hdev, i,
4667 4716 USB_PORT_FEAT_C_PORT_LINK_STATE);
4668 4717 }
4669 4718 if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
4670 4719 dev_warn(hub_dev,
4671 4720 "config error on port %d\n",
4672 4721 i);
4673   - clear_port_feature(hub->hdev, i,
  4722 + usb_clear_port_feature(hub->hdev, i,
4674 4723 USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
4675 4724 }
4676 4725  
... ... @@ -4954,7 +5003,7 @@
4954 5003 dev_dbg(&udev->dev, "%s for root hub!\n", __func__);
4955 5004 return -EISDIR;
4956 5005 }
4957   - parent_hub = hdev_to_hub(parent_hdev);
  5006 + parent_hub = usb_hub_to_struct_hub(parent_hdev);
4958 5007  
4959 5008 /* Disable LPM and LTM while we reset the device and reinstall the alt
4960 5009 * settings. Device-initiated LPM settings, and system exit latency
... ... @@ -5210,7 +5259,7 @@
5210 5259 struct usb_device *usb_hub_find_child(struct usb_device *hdev,
5211 5260 int port1)
5212 5261 {
5213   - struct usb_hub *hub = hdev_to_hub(hdev);
  5262 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
5214 5263  
5215 5264 if (port1 < 1 || port1 > hdev->maxchild)
5216 5265 return NULL;
... ... @@ -5227,7 +5276,7 @@
5227 5276 void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
5228 5277 enum usb_port_connect_type type)
5229 5278 {
5230   - struct usb_hub *hub = hdev_to_hub(hdev);
  5279 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
5231 5280  
5232 5281 hub->ports[port1 - 1]->connect_type = type;
5233 5282 }
... ... @@ -5243,7 +5292,7 @@
5243 5292 enum usb_port_connect_type
5244 5293 usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
5245 5294 {
5246   - struct usb_hub *hub = hdev_to_hub(hdev);
  5295 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
5247 5296  
5248 5297 return hub->ports[port1 - 1]->connect_type;
5249 5298 }
... ... @@ -5301,7 +5350,7 @@
5301 5350 acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
5302 5351 int port1)
5303 5352 {
5304   - struct usb_hub *hub = hdev_to_hub(hdev);
  5353 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
5305 5354  
5306 5355 return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
5307 5356 }
drivers/usb/core/hub.h
... ... @@ -80,6 +80,8 @@
80 80 * @port_owner: port's owner
81 81 * @connect_type: port's connect type
82 82 * @portnum: port index num based one
  83 + * @power_is_on: port's power state
  84 + * @did_runtime_put: port has done pm_runtime_put().
83 85 */
84 86 struct usb_port {
85 87 struct usb_device *child;
... ... @@ -87,6 +89,8 @@
87 89 struct dev_state *port_owner;
88 90 enum usb_port_connect_type connect_type;
89 91 u8 portnum;
  92 + unsigned power_is_on:1;
  93 + unsigned did_runtime_put:1;
90 94 };
91 95  
92 96 #define to_usb_port(_dev) \
... ... @@ -98,4 +102,21 @@
98 102 int port1);
99 103 extern int usb_hub_set_port_power(struct usb_device *hdev,
100 104 int port1, bool set);
  105 +extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
  106 +extern int hub_port_debounce(struct usb_hub *hub, int port1,
  107 + bool must_be_connected);
  108 +extern int usb_clear_port_feature(struct usb_device *hdev,
  109 + int port1, int feature);
  110 +
  111 +static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
  112 + int port1)
  113 +{
  114 + return hub_port_debounce(hub, port1, true);
  115 +}
  116 +
  117 +static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
  118 + int port1)
  119 +{
  120 + return hub_port_debounce(hub, port1, false);
  121 +}
drivers/usb/core/port.c
... ... @@ -77,10 +77,36 @@
77 77 struct usb_port *port_dev = to_usb_port(dev);
78 78 struct usb_device *hdev = to_usb_device(dev->parent->parent);
79 79 struct usb_interface *intf = to_usb_interface(dev->parent);
  80 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
  81 + int port1 = port_dev->portnum;
80 82 int retval;
81 83  
  84 + if (!hub)
  85 + return -EINVAL;
  86 +
82 87 usb_autopm_get_interface(intf);
83   - retval = usb_hub_set_port_power(hdev, port_dev->portnum, true);
  88 + set_bit(port1, hub->busy_bits);
  89 +
  90 + retval = usb_hub_set_port_power(hdev, port1, true);
  91 + if (port_dev->child && !retval) {
  92 + /*
  93 + * Wait for usb hub port to be reconnected in order to make
  94 + * the resume procedure successful.
  95 + */
  96 + retval = hub_port_debounce_be_connected(hub, port1);
  97 + if (retval < 0) {
  98 + dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
  99 + retval);
  100 + goto out;
  101 + }
  102 + usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
  103 +
  104 + /* Set return value to 0 if debounce successful */
  105 + retval = 0;
  106 + }
  107 +
  108 +out:
  109 + clear_bit(port1, hub->busy_bits);
84 110 usb_autopm_put_interface(intf);
85 111 return retval;
86 112 }
87 113  
88 114  
... ... @@ -90,14 +116,23 @@
90 116 struct usb_port *port_dev = to_usb_port(dev);
91 117 struct usb_device *hdev = to_usb_device(dev->parent->parent);
92 118 struct usb_interface *intf = to_usb_interface(dev->parent);
  119 + struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
  120 + int port1 = port_dev->portnum;
93 121 int retval;
94 122  
  123 + if (!hub)
  124 + return -EINVAL;
  125 +
95 126 if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
96 127 == PM_QOS_FLAGS_ALL)
97 128 return -EAGAIN;
98 129  
99 130 usb_autopm_get_interface(intf);
100   - retval = usb_hub_set_port_power(hdev, port_dev->portnum, false);
  131 + set_bit(port1, hub->busy_bits);
  132 + retval = usb_hub_set_port_power(hdev, port1, false);
  133 + usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
  134 + usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
  135 + clear_bit(port1, hub->busy_bits);
101 136 usb_autopm_put_interface(intf);
102 137 return retval;
103 138 }
... ... @@ -130,6 +165,7 @@
130 165  
131 166 hub->ports[port1 - 1] = port_dev;
132 167 port_dev->portnum = port1;
  168 + port_dev->power_is_on = true;
133 169 port_dev->dev.parent = hub->intfdev;
134 170 port_dev->dev.groups = port_dev_group;
135 171 port_dev->dev.type = &usb_port_device_type;