aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hub.c39
-rw-r--r--drivers/usb/core/hub.h3
2 files changed, 34 insertions, 8 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 4191db32f12c..c857471519e3 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2848,6 +2848,15 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev)
2848 USB_CTRL_SET_TIMEOUT); 2848 USB_CTRL_SET_TIMEOUT);
2849} 2849}
2850 2850
2851/* Count of wakeup-enabled devices at or below udev */
2852static unsigned wakeup_enabled_descendants(struct usb_device *udev)
2853{
2854 struct usb_hub *hub = usb_hub_to_struct_hub(udev);
2855
2856 return udev->do_remote_wakeup +
2857 (hub ? hub->wakeup_enabled_descendants : 0);
2858}
2859
2851/* 2860/*
2852 * usb_port_suspend - suspend a usb device's upstream port 2861 * usb_port_suspend - suspend a usb device's upstream port
2853 * @udev: device that's no longer in active use, not a root hub 2862 * @udev: device that's no longer in active use, not a root hub
@@ -2888,8 +2897,8 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev)
2888 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd 2897 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd
2889 * timer, no SRP, no requests through sysfs. 2898 * timer, no SRP, no requests through sysfs.
2890 * 2899 *
2891 * If Runtime PM isn't enabled or used, non-SuperSpeed devices really get 2900 * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get
2892 * suspended only when their bus goes into global suspend (i.e., the root 2901 * suspended until their bus goes into global suspend (i.e., the root
2893 * hub is suspended). Nevertheless, we change @udev->state to 2902 * hub is suspended). Nevertheless, we change @udev->state to
2894 * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual 2903 * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual
2895 * upstream port setting is stored in @udev->port_is_suspended. 2904 * upstream port setting is stored in @udev->port_is_suspended.
@@ -2960,15 +2969,21 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2960 /* see 7.1.7.6 */ 2969 /* see 7.1.7.6 */
2961 if (hub_is_superspeed(hub->hdev)) 2970 if (hub_is_superspeed(hub->hdev))
2962 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3); 2971 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
2963 else if (PMSG_IS_AUTO(msg)) 2972
2964 status = set_port_feature(hub->hdev, port1,
2965 USB_PORT_FEAT_SUSPEND);
2966 /* 2973 /*
2967 * For system suspend, we do not need to enable the suspend feature 2974 * For system suspend, we do not need to enable the suspend feature
2968 * on individual USB-2 ports. The devices will automatically go 2975 * on individual USB-2 ports. The devices will automatically go
2969 * into suspend a few ms after the root hub stops sending packets. 2976 * into suspend a few ms after the root hub stops sending packets.
2970 * The USB 2.0 spec calls this "global suspend". 2977 * The USB 2.0 spec calls this "global suspend".
2978 *
2979 * However, many USB hubs have a bug: They don't relay wakeup requests
2980 * from a downstream port if the port's suspend feature isn't on.
2981 * Therefore we will turn on the suspend feature if udev or any of its
2982 * descendants is enabled for remote wakeup.
2971 */ 2983 */
2984 else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
2985 status = set_port_feature(hub->hdev, port1,
2986 USB_PORT_FEAT_SUSPEND);
2972 else { 2987 else {
2973 really_suspend = false; 2988 really_suspend = false;
2974 status = 0; 2989 status = 0;
@@ -3003,15 +3018,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
3003 if (!PMSG_IS_AUTO(msg)) 3018 if (!PMSG_IS_AUTO(msg))
3004 status = 0; 3019 status = 0;
3005 } else { 3020 } else {
3006 /* device has up to 10 msec to fully suspend */
3007 dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", 3021 dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
3008 (PMSG_IS_AUTO(msg) ? "auto-" : ""), 3022 (PMSG_IS_AUTO(msg) ? "auto-" : ""),
3009 udev->do_remote_wakeup); 3023 udev->do_remote_wakeup);
3010 usb_set_device_state(udev, USB_STATE_SUSPENDED);
3011 if (really_suspend) { 3024 if (really_suspend) {
3012 udev->port_is_suspended = 1; 3025 udev->port_is_suspended = 1;
3026
3027 /* device has up to 10 msec to fully suspend */
3013 msleep(10); 3028 msleep(10);
3014 } 3029 }
3030 usb_set_device_state(udev, USB_STATE_SUSPENDED);
3015 } 3031 }
3016 3032
3017 /* 3033 /*
@@ -3293,7 +3309,11 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
3293 unsigned port1; 3309 unsigned port1;
3294 int status; 3310 int status;
3295 3311
3296 /* Warn if children aren't already suspended */ 3312 /*
3313 * Warn if children aren't already suspended.
3314 * Also, add up the number of wakeup-enabled descendants.
3315 */
3316 hub->wakeup_enabled_descendants = 0;
3297 for (port1 = 1; port1 <= hdev->maxchild; port1++) { 3317 for (port1 = 1; port1 <= hdev->maxchild; port1++) {
3298 struct usb_device *udev; 3318 struct usb_device *udev;
3299 3319
@@ -3303,6 +3323,9 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
3303 if (PMSG_IS_AUTO(msg)) 3323 if (PMSG_IS_AUTO(msg))
3304 return -EBUSY; 3324 return -EBUSY;
3305 } 3325 }
3326 if (udev)
3327 hub->wakeup_enabled_descendants +=
3328 wakeup_enabled_descendants(udev);
3306 } 3329 }
3307 3330
3308 if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) { 3331 if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 6508e02b3dac..4e4790dea343 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -59,6 +59,9 @@ struct usb_hub {
59 struct usb_tt tt; /* Transaction Translator */ 59 struct usb_tt tt; /* Transaction Translator */
60 60
61 unsigned mA_per_port; /* current for each child */ 61 unsigned mA_per_port; /* current for each child */
62#ifdef CONFIG_PM
63 unsigned wakeup_enabled_descendants;
64#endif
62 65
63 unsigned limited_power:1; 66 unsigned limited_power:1;
64 unsigned quiescing:1; 67 unsigned quiescing:1;