USB3: unable to enumerate, device not accepting address

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

 



Hi,

I have 2 very similar USB3 devices that stopped working sometime after
kernel version 3.3 - they fail to enumerate unless I reload xhci_hcd
driver.

These are the devices:
http://www.agestar.com/en/Products/Docking-Station/USB3-0/974-usb30esata-to-2535-sata-hdd-docking-station.html
http://www.agestar.com/en/Products/Docking-Station/USB3-0/980-usb30esata-to-2535-sata-hdd-docking-station.html
Basically it's some eSATA->USB3 bridge JMicron chip (I'm guessing it's
the same one in both devices):
Bus 009 Device 002: ID 152d:2509 JMicron Technology Corp. / JMicron USA Technology Corp. JMS539 SuperSpeed SATA II 3.0G Bridge

This is USB controller I am testing them with:
01:00.0 USB controller: NEC Corporation uPD720200 USB 3.0 Host Controller (rev 03)
I even tried upgrading firmware, with no result

It appears in system as 2 buses (lsusb -t output):
/:  Bus 09.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 5000M
/:  Bus 08.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
One for SuperSpeed devices, another for slower.

So here's dmesg for when it fails:
[  195.207408] hub 8-0:1.0: unable to enumerate USB device on port 1
[  196.016556] usb 9-1: device not accepting address 2, error -22
[  196.520572] hub 9-0:1.0: unable to enumerate USB device on port 1

And here how it should look (when it works):
[   61.686218] hub 8-0:1.0: unable to enumerate USB device on port 1
[   63.620211] usb 9-1: new SuperSpeed USB device number 2 using xhci_hcd
[   63.636918] usb 9-1: New USB device found, idVendor=152d, idProduct=2509
[   63.636932] usb 9-1: New USB device strings: Mfr=1, Product=11, SerialNumber=3
[   63.636942] usb 9-1: Product: Usb production
[   63.636950] usb 9-1: Manufacturer: JMicron
[   63.636956] usb 9-1: SerialNumber: 00A1234578EA
[   63.638549] scsi5 : usb-storage 9-1:1.0
[   64.926983] scsi 5:0:0:0: Direct-Access     Jmicron  Corp.            0000 PQ: 0 ANSI: 5
[   64.927925] sd 5:0:0:0: Attached scsi generic sg6 type 0
[   64.929912] sd 5:0:0:0: [sdg] Very big device. Trying to use READ CAPACITY(16).
[   64.930353] sd 5:0:0:0: [sdg] 5860533168 512-byte logical blocks: (3.00 TB/2.72 TiB)
[   64.931031] sd 5:0:0:0: [sdg] Write Protect is off
[   64.931042] sd 5:0:0:0: [sdg] Mode Sense: 28 00 00 00
[   64.931764] sd 5:0:0:0: [sdg] No Caching mode page present
[   64.931772] sd 5:0:0:0: [sdg] Assuming drive cache: write through
[   64.932486] sd 5:0:0:0: [sdg] Very big device. Trying to use READ CAPACITY(16).
[   64.933992] sd 5:0:0:0: [sdg] No Caching mode page present
[   64.933997] sd 5:0:0:0: [sdg] Assuming drive cache: write through
[   64.989015]  sdg: sdg1 sdg2 sdg3 sdg4
[   64.992112] sd 5:0:0:0: [sdg] Very big device. Trying to use READ CAPACITY(16).
[   64.993885] sd 5:0:0:0: [sdg] No Caching mode page present
[   64.993898] sd 5:0:0:0: [sdg] Assuming drive cache: write through
[   64.993909] sd 5:0:0:0: [sdg] Attached SCSI disk
First line doesn't always appear, might depend on kernel version, I'm
not sure.

I managed to bisect this down to this commit:
beabe20445c60322719d8f58e9eb9dd4660c1b3e
(it's from 3.4 branch, included in 3.4.36 release, upstream commit id
from commit message seems to be invalid, at least it's missing one
character).
I backported reverse of this commit to 3.17.1 (I can't run 3.4 kernel
due to different issues) and it helps with this issue. Patch attached
in case it is helpful, sorry though for whitespace mess, I used nano:)

I doubt that just reverting is acceptable solution for mainstream
kernel, so I'm willing to test some other patches (on top of 3.17.1
would be best) or provide additional information so that this issue
could be fixed in next releases.
--- drivers/usb/core/hub.c.orig	2014-10-30 21:44:50.000746074 +0300
+++ drivers/usb/core/hub.c	2014-10-30 22:12:01.561256025 +0300
@@ -2664,56 +2664,95 @@
 	if ((portstatus & USB_PORT_STAT_RESET))
 		return -EBUSY;
 
-	if (hub_port_warm_reset_required(hub, port1, portstatus))
-		return -ENOTCONN;
-
-	/* Device went away? */
-	if (!(portstatus & USB_PORT_STAT_CONNECTION))
-		return -ENOTCONN;
-
-	/* bomb out completely if the connection bounced.  A USB 3.0
-	 * connection may bounce if multiple warm resets were issued,
-	 * but the device may have successfully re-connected. Ignore it.
+	/* Some buggy devices require a warm reset to be issued even
+	 * when the port appears not to be connected.
 	 */
-	if (!hub_is_superspeed(hub->hdev) &&
-			(portchange & USB_PORT_STAT_C_CONNECTION))
-		return -ENOTCONN;
-
-	if (!(portstatus & USB_PORT_STAT_ENABLE))
-		return -EBUSY;
+               if (!warm) {
+                       /*
+                        * Some buggy devices can cause an NEC host controller
+                        * to transition to the "Error" state after a hot port
+                        * reset.  This will show up as the port state in
+                        * "Inactive", and the port may also report a
+                        * disconnect.  Forcing a warm port reset seems to make
+                        * the device work.
+                        *
+                        * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
+                        */
+                       if (hub_port_warm_reset_required(hub, port1, portstatus)) {
+                               int ret;
+
+                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
+                                       usb_clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_CONNECTION);
+                               if (portchange & USB_PORT_STAT_C_LINK_STATE)
+                                       usb_clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+                               if (portchange & USB_PORT_STAT_C_RESET)
+                                       usb_clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_RESET);
+                               dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+                                               port1);
+                               ret = hub_port_reset(hub, port1,
+                                               udev, HUB_BH_RESET_TIME,
+                                               true);
+                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
+                                       usb_clear_port_feature(hub->hdev, port1,
+                                                       USB_PORT_FEAT_C_CONNECTION);
+                               return ret;
+                       }
+                       /* Device went away? */
+                       if (!(portstatus & USB_PORT_STAT_CONNECTION))
+                               return -ENOTCONN;
+
+                       /* bomb out completely if the connection bounced */
+                       if ((portchange & USB_PORT_STAT_C_CONNECTION))
+                               return -ENOTCONN;
+
+                       if ((portstatus & USB_PORT_STAT_ENABLE)) {
+                               if (!udev)
+                                       return 0;
+
+                               if (hub_is_wusb(hub))
+                                       udev->speed = USB_SPEED_WIRELESS;
+                               else if (hub_is_superspeed(hub->hdev))
+                                       udev->speed = USB_SPEED_SUPER;
+                               else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+                                       udev->speed = USB_SPEED_HIGH;
+                               else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+                                       udev->speed = USB_SPEED_LOW;
+                               else
+                                       udev->speed = USB_SPEED_FULL;
+				return 0;
+                       }
+               } else {
+                       if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+                                       hub_port_warm_reset_required(hub, port1,
+                                               portstatus))
+                               return -ENOTCONN;
 
-	if (!udev)
 		return 0;
-
-	if (hub_is_wusb(hub))
-		udev->speed = USB_SPEED_WIRELESS;
-	else if (hub_is_superspeed(hub->hdev))
-		udev->speed = USB_SPEED_SUPER;
-	else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
-		udev->speed = USB_SPEED_HIGH;
-	else if (portstatus & USB_PORT_STAT_LOW_SPEED)
-		udev->speed = USB_SPEED_LOW;
-	else
-		udev->speed = USB_SPEED_FULL;
-	return 0;
+	}
+	return -EBUSY;
 }
 
 static void hub_port_finish_reset(struct usb_hub *hub, int port1,
-			struct usb_device *udev, int *status)
+			struct usb_device *udev, int *status, bool warm)
 {
 	switch (*status) {
 	case 0:
-		/* TRSTRCY = 10 ms; plus some extra */
-		msleep(10 + 40);
-		if (udev) {
-			struct usb_hcd *hcd = bus_to_hcd(udev->bus);
-
-			update_devnum(udev, 0);
-			/* The xHC may think the device is already reset,
-			 * so ignore the status.
-			 */
-			if (hcd->driver->reset_device)
-				hcd->driver->reset_device(hcd, udev);
+               if (!warm) {
+                       struct usb_hcd *hcd;
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       msleep(10 + 40);
+                       if (udev) {
+                               update_devnum(udev, 0);
+                               hcd = bus_to_hcd(udev->bus);
+                               /* The xHC may think the device is already
+                                * reset, so ignore the status.
+                                */
+                               if (hcd->driver->reset_device)
+                                       hcd->driver->reset_device(hcd, udev);
+                       }
 		}
 		/* FALL THROUGH */
 	case -ENOTCONN:
@@ -2725,10 +2764,8 @@
 					USB_PORT_FEAT_C_BH_PORT_RESET);
 			usb_clear_port_feature(hub->hdev, port1,
 					USB_PORT_FEAT_C_PORT_LINK_STATE);
-			usb_clear_port_feature(hub->hdev, port1,
-					USB_PORT_FEAT_C_CONNECTION);
 		}
-		if (udev)
+		if (!warm && udev)
 			usb_set_device_state(udev, *status
 					? USB_STATE_NOTATTACHED
 					: USB_STATE_DEFAULT);
@@ -2789,34 +2826,10 @@
 						status);
 		}
 
-		/* Check for disconnect or reset */
+		/* return on disconnect or reset */
 		if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
-			hub_port_finish_reset(hub, port1, udev, &status);
-
-			if (!hub_is_superspeed(hub->hdev))
-				goto done;
-
-			/*
-			 * If a USB 3.0 device migrates from reset to an error
-			 * state, re-issue the warm reset.
-			 */
-			if (hub_port_status(hub, port1,
-					&portstatus, &portchange) < 0)
-				goto done;
-
-			if (!hub_port_warm_reset_required(hub, port1,
-					portstatus))
-				goto done;
-
-			/*
-			 * If the port is in SS.Inactive or Compliance Mode, the
-			 * hot or warm reset failed.  Try another warm reset.
-			 */
-			if (!warm) {
-				dev_dbg(&port_dev->dev,
-						"hot reset failed, warm reset\n");
-				warm = true;
-			}
+                       hub_port_finish_reset(hub, port1, udev, &status, warm);
+                       goto done;
 		}
 
 		dev_dbg(&port_dev->dev,

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

  Powered by Linux