[RFT] usb: Fix TX errors on devices with unsupported LPM states.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Don,

The WD drive arrived today, but I can't reproduce your I/O errors on
3.6.  I didn't try with 3.5 yet.  However, I did notice that the device
really doesn't want to enter U1, but it will enter U2.

Can you test the following patch with your device and see if it helps?
If that doesn't work, I suggest getting a high-quality new cable and
seeing if that helps the transfer issues.

Gabor, this might fix your issues as well, and should allow your system
to consume less power than completely disabling LPM.

Sarah Sharp

>8-------------------------------------------------------------8<

USB 3.0 devices are required to support Link PM.  However, some of
them don't support U1, or don't support U2, or don't support either.
There is no way in the USB 3.0 specification to say that a device
doesn't support U1 or U2, so these devices set the U1 or U2 exit
latency to the maximum possible values (10ms for U1, 2047ms for U2).

With the old code, when we enabled hub-initiated U1 or U2 for a device
on the roothub, the host would send LGO_U1 and LGO_U2 every 10ms or
2047ms, respectively.  The device would always send refuse to go into
the lower power link state, and would send an LAUX back.

The constant U1/U2 refusals would fill an idle bus with link commands.
This caused transfer errors and timeouts with a Western Digital My
Passport Essential 500 GB USB 3.0 hard drive (PID:VID 1058:0740).  The
lsusb shows that device has the maximum possible device exit latency
for U1, but has a non-max U2 exit latency:

  SuperSpeed USB Device Capability:
    bLength                10
    bDescriptorType        16
    bDevCapabilityType      3
    bmAttributes         0x00
      Latency Tolerance Messages (LTM) Supported
    wSpeedsSupported   0x000e
      Device can operate at Full Speed (12Mbps)
      Device can operate at High Speed (480Mbps)
      Device can operate at SuperSpeed (5Gbps)
    bFunctionalitySupport   1
      Lowest fully-functional device speed is Full Speed (12Mbps)
    bU1DevExitLat          10 micro seconds
    bU2DevExitLat          32 micro seconds

Change the LPM enabling code to look for the maximum U1 and U2 exit
latencies.  If we find those magic values, don't enable hub-initiated
transitions to U1 or U2.  Instead, let the device initiate the lower
power link state.  That way, if a device does support U1 or U2 and it
truely needs the maximum exit latency, we can still save power.

With this patch, the roothub lsusb output shows the device is happy to
go into U2, just not U1:

 Hub Port Status:
   Port 1: 0000.0243 5Gbps power U2 enable connect
   Port 2: 0000.0263 5Gbps power suspend enable connect
   Port 3: 0000.02a0 5Gbps power Rx.Detect
   Port 4: 0000.02a0 5Gbps power Rx.Detect

This patch should be backported to stable kernels as old as 3.5, that
contain the commit 1ea7e0e8e3d0f50901d335ea4178ab2aa8c88201 "USB: Add
support to enable/disable USB3 link states."

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
---
 drivers/usb/core/hub.c  |   19 +++++++++++++++++++
 include/linux/usb/ch9.h |    2 ++
 2 files changed, 21 insertions(+)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 128a804..e1065ed 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3353,6 +3353,8 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
 		enum usb3_link_state state)
 {
 	int timeout;
+	__u8 u1_mel;
+	__le16 u2_mel;
 
 	/* We allow the host controller to set the U1/U2 timeout internally
 	 * first, so that it can change its schedule to account for the
@@ -3372,6 +3374,23 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
 		return;
 	}
 
+	/*
+	 * Magic maximum device exit latency values may mean the device doesn't
+	 * support U1 or U2.  If hub-initiated LPM is enabled, the device will
+	 * always refuse to go into U1 or U2, and will fill the bus with LAUX
+	 * link commands.  This can cause transfer errors, so only enable
+	 * device-initiated LPM.
+	 */
+	u1_mel = udev->bos->ss_cap->bU1devExitLat;
+	u2_mel = udev->bos->ss_cap->bU2DevExitLat;
+	if ((state == USB3_LPM_U1 && u1_mel == USB_U1_MAX_VALID_MEL) ||
+			(state == USB3_LPM_U2 &&
+			 le16_to_cpu(u2_mel) == USB_U2_MAX_VALID_MEL)) {
+		if (usb_set_device_initiated_lpm(udev, state, true))
+			hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
+		return;
+	}
+
 	if (usb_set_lpm_timeout(udev, state, timeout))
 		/* If we can't set the parent hub U1/U2 timeout,
 		 * device-initiated LPM won't be allowed either, so let the xHCI
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index d1d732c..edf28ac 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -828,7 +828,9 @@ struct usb_ss_cap_descriptor {		/* Link Power Management */
 #define USB_HIGH_SPEED_OPERATION	(1 << 2) /* High speed operation */
 #define USB_5GBPS_OPERATION		(1 << 3) /* Operation at 5Gbps */
 	__u8  bFunctionalitySupport;
+#define	USB_U1_MAX_VALID_MEL		0x0A	/* U1 MEL is 10us, max */
 	__u8  bU1devExitLat;
+#define	USB_U2_MAX_VALID_MEL		0x7FF	/* U2 MEL is 2047us, max */
 	__le16 bU2DevExitLat;
 } __attribute__((packed));
 
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux