diff options
| -rw-r--r-- | drivers/usb/core/hub.c | 39 | ||||
| -rw-r--r-- | drivers/usb/core/hub.h | 3 |
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 */ | ||
| 2852 | static 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; |
