1 From d7a6c0ce8d26412903c7981503bad9e1cc7c45d2 Mon Sep 17 00:00:00 2001
2 From: Kai-Heng Feng <kai.heng.feng@canonical.com>
3 Date: Sat, 12 Jan 2019 03:54:25 +0800
4 Subject: USB: Consolidate LPM checks to avoid enabling LPM twice
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
9 From: Kai-Heng Feng <kai.heng.feng@canonical.com>
11 commit d7a6c0ce8d26412903c7981503bad9e1cc7c45d2 upstream.
13 USB Bluetooth controller QCA ROME (0cf3:e007) sometimes stops working
15 [ 165.110742] Bluetooth: hci0: using NVM file: qca/nvm_usb_00000302.bin
16 [ 168.432065] Bluetooth: hci0: Failed to send body at 4 of 1953 (-110)
18 After some experiments, I found that disabling LPM can workaround the
21 On some platforms, the USB power is cut during S3, so the driver uses
22 reset-resume to resume the device. During port resume, LPM gets enabled
23 twice, by usb_reset_and_verify_device() and usb_port_resume().
25 Consolidate all checks into new LPM helpers to make sure LPM only gets
28 Fixes: de68bab4fa96 ("usb: Don't enable USB 2.0 Link PM by default.”)
29 Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
30 Cc: stable <stable@vger.kernel.org> # after much soaking
31 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
32 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
35 drivers/usb/core/driver.c | 11 ++++++++---
36 drivers/usb/core/hub.c | 12 ++++--------
37 drivers/usb/core/message.c | 3 +--
38 3 files changed, 13 insertions(+), 13 deletions(-)
40 --- a/drivers/usb/core/driver.c
41 +++ b/drivers/usb/core/driver.c
42 @@ -1893,9 +1893,6 @@ static int usb_set_usb2_hardware_lpm(str
43 struct usb_hcd *hcd = bus_to_hcd(udev->bus);
46 - if (enable && !udev->usb2_hw_lpm_allowed)
49 if (hcd->driver->set_usb2_hw_lpm) {
50 ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
52 @@ -1907,11 +1904,19 @@ static int usb_set_usb2_hardware_lpm(str
54 int usb_enable_usb2_hardware_lpm(struct usb_device *udev)
56 + if (!udev->usb2_hw_lpm_capable ||
57 + !udev->usb2_hw_lpm_allowed ||
58 + udev->usb2_hw_lpm_enabled)
61 return usb_set_usb2_hardware_lpm(udev, 1);
64 int usb_disable_usb2_hardware_lpm(struct usb_device *udev)
66 + if (!udev->usb2_hw_lpm_enabled)
69 return usb_set_usb2_hardware_lpm(udev, 0);
72 --- a/drivers/usb/core/hub.c
73 +++ b/drivers/usb/core/hub.c
74 @@ -3168,8 +3168,7 @@ int usb_port_suspend(struct usb_device *
77 /* disable USB2 hardware LPM */
78 - if (udev->usb2_hw_lpm_enabled == 1)
79 - usb_disable_usb2_hardware_lpm(udev);
80 + usb_disable_usb2_hardware_lpm(udev);
82 if (usb_disable_ltm(udev)) {
83 dev_err(&udev->dev, "Failed to disable LTM before suspend\n.");
84 @@ -3215,8 +3214,7 @@ int usb_port_suspend(struct usb_device *
87 /* Try to enable USB2 hardware LPM again */
88 - if (udev->usb2_hw_lpm_capable == 1)
89 - usb_enable_usb2_hardware_lpm(udev);
90 + usb_enable_usb2_hardware_lpm(udev);
92 if (udev->do_remote_wakeup)
93 (void) usb_disable_remote_wakeup(udev);
94 @@ -3499,8 +3497,7 @@ int usb_port_resume(struct usb_device *u
95 hub_port_logical_disconnect(hub, port1);
97 /* Try to enable USB2 hardware LPM */
98 - if (udev->usb2_hw_lpm_capable == 1)
99 - usb_enable_usb2_hardware_lpm(udev);
100 + usb_enable_usb2_hardware_lpm(udev);
102 /* Try to enable USB3 LTM and LPM */
103 usb_enable_ltm(udev);
104 @@ -5481,8 +5478,7 @@ static int usb_reset_and_verify_device(s
105 /* Disable USB2 hardware LPM.
106 * It will be re-enabled by the enumeration process.
108 - if (udev->usb2_hw_lpm_enabled == 1)
109 - usb_disable_usb2_hardware_lpm(udev);
110 + usb_disable_usb2_hardware_lpm(udev);
112 /* Disable LPM and LTM while we reset the device and reinstall the alt
113 * settings. Device-initiated LPM settings, and system exit latency
114 --- a/drivers/usb/core/message.c
115 +++ b/drivers/usb/core/message.c
116 @@ -1181,8 +1181,7 @@ void usb_disable_device(struct usb_devic
117 dev->actconfig->interface[i] = NULL;
120 - if (dev->usb2_hw_lpm_enabled == 1)
121 - usb_disable_usb2_hardware_lpm(dev);
122 + usb_disable_usb2_hardware_lpm(dev);
123 usb_unlocked_disable_lpm(dev);
124 usb_disable_ltm(dev);