summaryrefslogtreecommitdiff
path: root/include/linux/usb.h
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2012-05-03 01:25:52 +0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-05-19 02:41:59 +0400
commit8306095fd2c1100e8244c09bf560f97aca5a311d (patch)
tree1096f11806046e60f32496f1bac843a6f17b4c26 /include/linux/usb.h
parent1ea7e0e8e3d0f50901d335ea4178ab2aa8c88201 (diff)
downloadlinux-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/linux/usb.h')
-rw-r--r--include/linux/usb.h2
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)