Skip to content

Commit de68bab

Browse files
author
Sarah Sharp
committed
usb: Don't enable USB 2.0 Link PM by default.
How it's supposed to work: -------------------------- USB 2.0 Link PM is a lower power state that some newer USB 2.0 devices support. USB 3.0 devices certified by the USB-IF are required to support it if they are plugged into a USB 2.0 only port, or a USB 2.0 cable is used. USB 2.0 Link PM requires both a USB device and a host controller that supports USB 2.0 hardware-enabled LPM. USB 2.0 Link PM is designed to be enabled once by software, and the host hardware handles transitions to the L1 state automatically. The premise of USB 2.0 Link PM is to be able to put the device into a lower power link state when the bus is idle or the device NAKs USB IN transfers for a specified amount of time. ...but hardware is broken: -------------------------- It turns out many USB 3.0 devices claim to support USB 2.0 Link PM (by setting the LPM bit in their USB 2.0 BOS descriptor), but they don't actually implement it correctly. This manifests as the USB device refusing to respond to transfers when it is plugged into a USB 2.0 only port under the Haswell-ULT/Lynx Point LP xHCI host. These devices pass the xHCI driver's simple test to enable USB 2.0 Link PM, wait for the port to enter L1, and then bring it back into L0. They only start to break when L1 entry is interleaved with transfers. Some devices then fail to respond to the next control transfer (usually a Set Configuration). This results in devices never enumerating. Other mass storage devices (such as a later model Western Digital My Passport USB 3.0 hard drive) respond fine to going into L1 between control transfers. They ACK the entry, come out of L1 when the host needs to send a control transfer, and respond properly to those control transfers. However, when the first READ10 SCSI command is sent, the device NAKs the data phase while it's reading from the spinning disk. Eventually, the host requests to put the link into L1, and the device ACKs that request. Then it never responds to the data phase of the READ10 command. This results in not being able to read from the drive. Some mass storage devices (like the Corsair Survivor USB 3.0 flash drive) are well behaved. They ACK the entry into L1 during control transfers, and when SCSI commands start coming in, they NAK the requests to go into L1, because they need to be at full power. Not all USB 3.0 devices advertise USB 2.0 link PM support. My Point Grey USB 3.0 webcam advertises itself as a USB 2.1 device, but doesn't have a USB 2.0 BOS descriptor, so we don't enable USB 2.0 Link PM. I suspect that means the device isn't certified. What do we do about it? ----------------------- There's really no good way for the kernel to test these devices. Therefore, the kernel needs to disable USB 2.0 Link PM by default, and distros will have to enable it by writing 1 to the sysfs file /sys/bus/usb/devices/../power/usb2_hardware_lpm. Rip out the xHCI Link PM test, since it's not sufficient to detect these buggy devices, and don't automatically enable LPM after the device is addressed. This patch should be backported to kernels as old as 3.11, that contain the commit a558ccd "usb: xhci: add USB2 Link power management BESL support". Without this fix, some USB 3.0 devices will not enumerate or work properly under USB 2.0 ports on Haswell-ULT systems. Signed-off-by: Sarah Sharp <[email protected]> Cc: [email protected]
1 parent 58e21f7 commit de68bab

File tree

6 files changed

+29
-156
lines changed

6 files changed

+29
-156
lines changed

drivers/usb/core/driver.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,9 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
17901790
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
17911791
int ret = -EPERM;
17921792

1793+
if (enable && !udev->usb2_hw_lpm_allowed)
1794+
return 0;
1795+
17931796
if (hcd->driver->set_usb2_hw_lpm) {
17941797
ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
17951798
if (!ret)

drivers/usb/core/hub.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5203,6 +5203,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
52035203

52045204
done:
52055205
/* Now that the alt settings are re-installed, enable LTM and LPM. */
5206+
usb_set_usb2_hardware_lpm(udev, 1);
52065207
usb_unlocked_enable_lpm(udev);
52075208
usb_enable_ltm(udev);
52085209
usb_release_bos_descriptor(udev);

drivers/usb/core/sysfs.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev,
458458
struct usb_device *udev = to_usb_device(dev);
459459
const char *p;
460460

461-
if (udev->usb2_hw_lpm_enabled == 1)
461+
if (udev->usb2_hw_lpm_allowed == 1)
462462
p = "enabled";
463463
else
464464
p = "disabled";
@@ -478,8 +478,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
478478

479479
ret = strtobool(buf, &value);
480480

481-
if (!ret)
481+
if (!ret) {
482+
udev->usb2_hw_lpm_allowed = value;
482483
ret = usb_set_usb2_hardware_lpm(udev, value);
484+
}
483485

484486
usb_unlock_device(udev);
485487

drivers/usb/host/xhci-mem.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,9 +1693,7 @@ void xhci_free_command(struct xhci_hcd *xhci,
16931693
void xhci_mem_cleanup(struct xhci_hcd *xhci)
16941694
{
16951695
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
1696-
struct dev_info *dev_info, *next;
16971696
struct xhci_cd *cur_cd, *next_cd;
1698-
unsigned long flags;
16991697
int size;
17001698
int i, j, num_ports;
17011699

@@ -1756,13 +1754,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
17561754

17571755
scratchpad_free(xhci);
17581756

1759-
spin_lock_irqsave(&xhci->lock, flags);
1760-
list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) {
1761-
list_del(&dev_info->list);
1762-
kfree(dev_info);
1763-
}
1764-
spin_unlock_irqrestore(&xhci->lock, flags);
1765-
17661757
if (!xhci->rh_bw)
17671758
goto no_bw;
17681759

@@ -2231,7 +2222,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
22312222
u32 page_size, temp;
22322223
int i;
22332224

2234-
INIT_LIST_HEAD(&xhci->lpm_failed_devs);
22352225
INIT_LIST_HEAD(&xhci->cancel_cmd_list);
22362226

22372227
page_size = xhci_readl(xhci, &xhci->op_regs->page_size);

drivers/usb/host/xhci.c

Lines changed: 18 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -4013,133 +4013,6 @@ static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev)
40134013
return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm);
40144014
}
40154015

4016-
static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
4017-
struct usb_device *udev)
4018-
{
4019-
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
4020-
struct dev_info *dev_info;
4021-
__le32 __iomem **port_array;
4022-
__le32 __iomem *addr, *pm_addr;
4023-
u32 temp, dev_id;
4024-
unsigned int port_num;
4025-
unsigned long flags;
4026-
int hird;
4027-
int ret;
4028-
4029-
if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
4030-
!udev->lpm_capable)
4031-
return -EINVAL;
4032-
4033-
/* we only support lpm for non-hub device connected to root hub yet */
4034-
if (!udev->parent || udev->parent->parent ||
4035-
udev->descriptor.bDeviceClass == USB_CLASS_HUB)
4036-
return -EINVAL;
4037-
4038-
spin_lock_irqsave(&xhci->lock, flags);
4039-
4040-
/* Look for devices in lpm_failed_devs list */
4041-
dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 |
4042-
le16_to_cpu(udev->descriptor.idProduct);
4043-
list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) {
4044-
if (dev_info->dev_id == dev_id) {
4045-
ret = -EINVAL;
4046-
goto finish;
4047-
}
4048-
}
4049-
4050-
port_array = xhci->usb2_ports;
4051-
port_num = udev->portnum - 1;
4052-
4053-
if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) {
4054-
xhci_dbg(xhci, "invalid port number %d\n", udev->portnum);
4055-
ret = -EINVAL;
4056-
goto finish;
4057-
}
4058-
4059-
/*
4060-
* Test USB 2.0 software LPM.
4061-
* FIXME: some xHCI 1.0 hosts may implement a new register to set up
4062-
* hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1
4063-
* in the June 2011 errata release.
4064-
*/
4065-
xhci_dbg(xhci, "test port %d software LPM\n", port_num);
4066-
/*
4067-
* Set L1 Device Slot and HIRD/BESL.
4068-
* Check device's USB 2.0 extension descriptor to determine whether
4069-
* HIRD or BESL shoule be used. See USB2.0 LPM errata.
4070-
*/
4071-
pm_addr = port_array[port_num] + PORTPMSC;
4072-
hird = xhci_calculate_hird_besl(xhci, udev);
4073-
temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
4074-
xhci_writel(xhci, temp, pm_addr);
4075-
4076-
/* Set port link state to U2(L1) */
4077-
addr = port_array[port_num];
4078-
xhci_set_link_state(xhci, port_array, port_num, XDEV_U2);
4079-
4080-
/* wait for ACK */
4081-
spin_unlock_irqrestore(&xhci->lock, flags);
4082-
msleep(10);
4083-
spin_lock_irqsave(&xhci->lock, flags);
4084-
4085-
/* Check L1 Status */
4086-
ret = xhci_handshake(xhci, pm_addr,
4087-
PORT_L1S_MASK, PORT_L1S_SUCCESS, 125);
4088-
if (ret != -ETIMEDOUT) {
4089-
/* enter L1 successfully */
4090-
temp = xhci_readl(xhci, addr);
4091-
xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n",
4092-
port_num, temp);
4093-
ret = 0;
4094-
} else {
4095-
temp = xhci_readl(xhci, pm_addr);
4096-
xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n",
4097-
port_num, temp & PORT_L1S_MASK);
4098-
ret = -EINVAL;
4099-
}
4100-
4101-
/* Resume the port */
4102-
xhci_set_link_state(xhci, port_array, port_num, XDEV_U0);
4103-
4104-
spin_unlock_irqrestore(&xhci->lock, flags);
4105-
msleep(10);
4106-
spin_lock_irqsave(&xhci->lock, flags);
4107-
4108-
/* Clear PLC */
4109-
xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC);
4110-
4111-
/* Check PORTSC to make sure the device is in the right state */
4112-
if (!ret) {
4113-
temp = xhci_readl(xhci, addr);
4114-
xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp);
4115-
if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) ||
4116-
(temp & PORT_PLS_MASK) != XDEV_U0) {
4117-
xhci_dbg(xhci, "port L1 resume fail\n");
4118-
ret = -EINVAL;
4119-
}
4120-
}
4121-
4122-
if (ret) {
4123-
/* Insert dev to lpm_failed_devs list */
4124-
xhci_warn(xhci, "device LPM test failed, may disconnect and "
4125-
"re-enumerate\n");
4126-
dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC);
4127-
if (!dev_info) {
4128-
ret = -ENOMEM;
4129-
goto finish;
4130-
}
4131-
dev_info->dev_id = dev_id;
4132-
INIT_LIST_HEAD(&dev_info->list);
4133-
list_add(&dev_info->list, &xhci->lpm_failed_devs);
4134-
} else {
4135-
xhci_ring_device(xhci, udev->slot_id);
4136-
}
4137-
4138-
finish:
4139-
spin_unlock_irqrestore(&xhci->lock, flags);
4140-
return ret;
4141-
}
4142-
41434016
int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
41444017
struct usb_device *udev, int enable)
41454018
{
@@ -4267,24 +4140,26 @@ static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
42674140
int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
42684141
{
42694142
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
4270-
int ret;
42714143
int portnum = udev->portnum - 1;
42724144

4273-
ret = xhci_usb2_software_lpm_test(hcd, udev);
4274-
if (!ret) {
4275-
xhci_dbg(xhci, "software LPM test succeed\n");
4276-
if (xhci->hw_lpm_support == 1 &&
4277-
xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) {
4278-
udev->usb2_hw_lpm_capable = 1;
4279-
udev->l1_params.timeout = XHCI_L1_TIMEOUT;
4280-
udev->l1_params.besl = XHCI_DEFAULT_BESL;
4281-
if (xhci_check_usb2_port_capability(xhci, portnum,
4282-
XHCI_BLC))
4283-
udev->usb2_hw_lpm_besl_capable = 1;
4284-
ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
4285-
if (!ret)
4286-
udev->usb2_hw_lpm_enabled = 1;
4287-
}
4145+
if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
4146+
!udev->lpm_capable)
4147+
return 0;
4148+
4149+
/* we only support lpm for non-hub device connected to root hub yet */
4150+
if (!udev->parent || udev->parent->parent ||
4151+
udev->descriptor.bDeviceClass == USB_CLASS_HUB)
4152+
return 0;
4153+
4154+
if (xhci->hw_lpm_support == 1 &&
4155+
xhci_check_usb2_port_capability(
4156+
xhci, portnum, XHCI_HLC)) {
4157+
udev->usb2_hw_lpm_capable = 1;
4158+
udev->l1_params.timeout = XHCI_L1_TIMEOUT;
4159+
udev->l1_params.besl = XHCI_DEFAULT_BESL;
4160+
if (xhci_check_usb2_port_capability(xhci, portnum,
4161+
XHCI_BLC))
4162+
udev->usb2_hw_lpm_besl_capable = 1;
42884163
}
42894164

42904165
return 0;

include/linux/usb.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,8 @@ struct usb3_lpm_parameters {
475475
* @lpm_capable: device supports LPM
476476
* @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
477477
* @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
478-
* @usb2_hw_lpm_enabled: USB2 hardware LPM enabled
478+
* @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
479+
* @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
479480
* @usb3_lpm_enabled: USB3 hardware LPM enabled
480481
* @string_langid: language ID for strings
481482
* @product: iProduct string, if present (static)
@@ -548,6 +549,7 @@ struct usb_device {
548549
unsigned usb2_hw_lpm_capable:1;
549550
unsigned usb2_hw_lpm_besl_capable:1;
550551
unsigned usb2_hw_lpm_enabled:1;
552+
unsigned usb2_hw_lpm_allowed:1;
551553
unsigned usb3_lpm_enabled:1;
552554
int string_langid;
553555

0 commit comments

Comments
 (0)