aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-09-14 17:24:52 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-09-20 15:33:50 -0400
commit10d674a82e553cb8a1f41027bb3c3e309b3f6804 (patch)
treec79c27a71672d51ed935ca23b8faeb507664a4a9
parent2b69899934c63b7b9432568584fb4c4a2924f40c (diff)
USB: When hot reset for USB3 fails, try warm reset.
When a hot reset (standard USB port reset) fails on a USB 3.0 port, the host controller transitions to the "Error" state. It reports the port link state as "Inactive", sets the link state change flag, and (if the device disconnects) also reports the disconnect and connect change status. It's also supposed to transition the link state to "RxDetect", but the NEC µPD720200 xHCI host does not. Unfortunately, Harald found that the combination of the NEC µPD720200 and a LogiLink USB 3.0 to SATA adapter triggered this issue. The USB core would reset the device, the port would go into this error state, and the device would never be enumerated. This combination works under Windows, but not under Linux. When a hot reset fails on a USB 3.0 port, and the link state is reported as Inactive, fall back to a warm port reset instead. Harald confirms that with a warm port reset (along with all the change bits being correctly cleared), the USB 3.0 device will successfully enumerate. Harald also had to add two other patches ("xhci: Set change bit when warm reset change is set." and "usbcore: refine warm reset logic") to make this setup work. Since the warm reset refinement patch is not destined for the stable kernels (it's too big), this patch should not be backported either. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=41752 Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Tested-by: Harald Brennich <harald.brennich@gmx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/core/hub.c43
1 files changed, 43 insertions, 0 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index aa336f736f0c..1c155123c32f 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2029,6 +2029,17 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
2029#define HUB_LONG_RESET_TIME 200 2029#define HUB_LONG_RESET_TIME 200
2030#define HUB_RESET_TIMEOUT 500 2030#define HUB_RESET_TIMEOUT 500
2031 2031
2032static int hub_port_reset(struct usb_hub *hub, int port1,
2033 struct usb_device *udev, unsigned int delay, bool warm);
2034
2035/* Is a USB 3.0 port in the Inactive state? */
2036static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
2037{
2038 return hub_is_superspeed(hub->hdev) &&
2039 (portstatus & USB_PORT_STAT_LINK_STATE) ==
2040 USB_SS_PORT_LS_SS_INACTIVE;
2041}
2042
2032static int hub_port_wait_reset(struct usb_hub *hub, int port1, 2043static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2033 struct usb_device *udev, unsigned int delay, bool warm) 2044 struct usb_device *udev, unsigned int delay, bool warm)
2034{ 2045{
@@ -2052,6 +2063,38 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2052 * when the port appears not to be connected. 2063 * when the port appears not to be connected.
2053 */ 2064 */
2054 if (!warm) { 2065 if (!warm) {
2066 /*
2067 * Some buggy devices can cause an NEC host controller
2068 * to transition to the "Error" state after a hot port
2069 * reset. This will show up as the port state in
2070 * "Inactive", and the port may also report a
2071 * disconnect. Forcing a warm port reset seems to make
2072 * the device work.
2073 *
2074 * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
2075 */
2076 if (hub_port_inactive(hub, portstatus)) {
2077 int ret;
2078
2079 if ((portchange & USB_PORT_STAT_C_CONNECTION))
2080 clear_port_feature(hub->hdev, port1,
2081 USB_PORT_FEAT_C_CONNECTION);
2082 if (portchange & USB_PORT_STAT_C_LINK_STATE)
2083 clear_port_feature(hub->hdev, port1,
2084 USB_PORT_FEAT_C_PORT_LINK_STATE);
2085 if (portchange & USB_PORT_STAT_C_RESET)
2086 clear_port_feature(hub->hdev, port1,
2087 USB_PORT_FEAT_C_RESET);
2088 dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
2089 port1);
2090 ret = hub_port_reset(hub, port1,
2091 udev, HUB_BH_RESET_TIME,
2092 true);
2093 if ((portchange & USB_PORT_STAT_C_CONNECTION))
2094 clear_port_feature(hub->hdev, port1,
2095 USB_PORT_FEAT_C_CONNECTION);
2096 return ret;
2097 }
2055 /* Device went away? */ 2098 /* Device went away? */
2056 if (!(portstatus & USB_PORT_STAT_CONNECTION)) 2099 if (!(portstatus & USB_PORT_STAT_CONNECTION))
2057 return -ENOTCONN; 2100 return -ENOTCONN;