diff options
author | Andiry Xu <andiry.xu@amd.com> | 2011-09-24 01:19:52 +0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-09-27 02:51:10 +0400 |
commit | 65580b4321eb36f16ae8b5987bfa1bb948fc5112 (patch) | |
tree | 12735f923ad670c405efb5ab34c188dc06ad15f7 /drivers/usb/core | |
parent | 9574323c39d1f8359a04843075d89c9f32d8b7e6 (diff) | |
download | linux-65580b4321eb36f16ae8b5987bfa1bb948fc5112.tar.xz |
xHCI: set USB2 hardware LPM
If the device pass the USB2 software LPM and the host supports hardware
LPM, enable hardware LPM for the device to let the host decide when to
put the link into lower power state.
If hardware LPM is enabled for a port and driver wants to put it into
suspend, it must first disable hardware LPM, resume the port into U0,
and then suspend the port.
Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/driver.c | 14 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 5 |
3 files changed, 28 insertions, 0 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 14b83f2a4e87..adf5ca8a2396 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev) return 0; } +int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + int ret = -EPERM; + + if (hcd->driver->set_usb2_hw_lpm) { + ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); + if (!ret) + udev->usb2_hw_lpm_enabled = enable; + } + + return ret; +} + #endif /* CONFIG_USB_SUSPEND */ struct bus_type usb_bus_type = { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4ffc3d1bd9e7..d6cc83249341 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2392,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } } + /* disable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(udev, 0); + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2603,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); + } else { + /* Try to enable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); } + return status; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0d023cd2c149..3888778582c4 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -82,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev); extern int usb_runtime_suspend(struct device *dev); extern int usb_runtime_resume(struct device *dev); extern int usb_runtime_idle(struct device *dev); +extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); #else @@ -96,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev) return 0; } +static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + return 0; +} #endif extern struct bus_type usb_bus_type; |