diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2012-05-03 01:25:52 +0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2012-05-19 02:41:59 +0400 |
commit | 8306095fd2c1100e8244c09bf560f97aca5a311d (patch) | |
tree | 1096f11806046e60f32496f1bac843a6f17b4c26 /include | |
parent | 1ea7e0e8e3d0f50901d335ea4178ab2aa8c88201 (diff) | |
download | linux-8306095fd2c1100e8244c09bf560f97aca5a311d.tar.xz |
USB: Disable USB 3.0 LPM in critical sections.
There are several places where the USB core needs to disable USB 3.0
Link PM:
- usb_bind_interface
- usb_unbind_interface
- usb_driver_claim_interface
- usb_port_suspend/usb_port_resume
- usb_reset_and_verify_device
- usb_set_interface
- usb_reset_configuration
- usb_set_configuration
Use the new LPM disable/enable functions to temporarily disable LPM
around these critical sections.
We need to protect the critical section around binding and unbinding USB
interface drivers. USB drivers may want to disable hub-initiated USB
3.0 LPM, which will change the value of the U1/U2 timeouts that the xHCI
driver will install. We need to disable LPM completely until the driver
is bound to the interface, and the driver has a chance to enable
whatever alternate interface setting it needs in its probe routine.
Then re-enable USB3 LPM, and recalculate the U1/U2 timeout values.
We also need to disable LPM in usb_driver_claim_interface,
because drivers like usbfs can bind to an interface through that
function. Note, there is no way currently for userspace drivers to
disable hub-initiated USB 3.0 LPM. Revisit this later.
When a driver is unbound, the U1/U2 timeouts may change because we are
unbinding the last driver that needed hub-initiated USB 3.0 LPM to be
disabled.
USB LPM must be disabled when a USB device is going to be suspended.
The USB 3.0 spec does not define a state transition from U1 or U2 into
U3, so we need to bring the device into U0 by disabling LPM before we
can place it into U3. Therefore, call usb_unlocked_disable_lpm() in
usb_port_suspend(), and call usb_unlocked_enable_lpm() in
usb_port_resume(). If the port suspend fails, make sure to re-enable
LPM by calling usb_unlocked_enable_lpm(), since usb_port_resume() will
not be called on a failed port suspend.
USB 3.0 devices lose their USB 3.0 LPM settings (including whether USB
device-initiated LPM is enabled) across device suspend. Therefore,
disable LPM before the device will be reset in
usb_reset_and_verify_device(), and re-enable LPM after the reset is
complete and the configuration/alt settings are re-installed.
The calculated U1/U2 timeout values are heavily dependent on what USB
device endpoints are currently enabled. When any of the enabled
endpoints on the device might change, due to a new configuration, or new
alternate interface setting, we need to first disable USB 3.0 LPM, add
or delete endpoints from the xHCI schedule, install the new interfaces
and alt settings, and then re-enable LPM. Do this in usb_set_interface,
usb_reset_configuration, and usb_set_configuration.
Basically, there is a call to disable and then enable LPM in all
functions that lock the bandwidth_mutex. One exception is
usb_disable_device, because the device is disconnecting or otherwise
going away, and we should not care about whether USB 3.0 LPM is enabled.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/usb.h | 2 |
1 files changed, 2 insertions, 0 deletions
diff --git a/include/linux/usb.h b/include/linux/usb.h index 40439dfd81a7..c19297a8779c 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -526,6 +526,7 @@ struct usb_device { unsigned lpm_capable:1; unsigned usb2_hw_lpm_capable:1; unsigned usb2_hw_lpm_enabled:1; + unsigned usb3_lpm_enabled:1; int string_langid; /* static strings from the device */ @@ -555,6 +556,7 @@ struct usb_device { struct usb3_lpm_parameters u1_params; struct usb3_lpm_parameters u2_params; unsigned lpm_disable_count; + unsigned hub_initiated_lpm_disable_count; }; #define to_usb_device(d) container_of(d, struct usb_device, dev) |