Re: problem with Roseweil eusb3 enclosure

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

 



On Sat, Nov 03, 2012 at 02:51:36AM -0400, covici@xxxxxxxxxxxxxx wrote:
> OK, increasing the buffer size gives me the full syslog, which is
> enclosed.  I am not sending the dmesg because I think we have it all
> now.

Thanks, that helped.  Your port is coming up in compliance mode shortly
after it's powered on, which is kind of odd.  The host controller seems
to need a longer-than-normal warm reset timeout in this case.  I'm not
sure if the longer timeout will actually help, so I've also added code
to try a different method of bringing the port out of Compliance Mode
(disabling the port and re-enabling it).

Can you try the attached patch?  I've only compile-tested it, so I'm not
sure if it will work.

Please send me dmesg from the start of the log with the patch applied,
even if it does work.  I'd like to figure out which of the two solutions
helped.

Sarah Sharp
>From 6c11c297263ec4c87d226b30755da24bdae87fe4 Mon Sep 17 00:00:00 2001
From: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Date: Mon, 5 Nov 2012 17:46:55 -0800
Subject: [PATCH] USB: Disable and re-enable port on Compliance Mode.

The xHCI 1.0 spec says in section 4.19.1.2.7 that software can
transition a port from Compliance Mode to Reset by setting the Warm Port
Reset bit.  However, the same section in the xHCI 0.96 spec does not
mention that state transition, even though the transition is shown in
the USB 3.0 roothub port status diagram.

When John Covici boots with a USB 3.0 device attached to his 0.96 xHCI
host controller, the port goes into Compliance Mode during host
controller initialization.  The USB core sets the warm port reset bit,
but the WPR change bit is not set until much later.  The port bounces
between RxDetect, Polling, and Compliance Mode continuously.

First, extend the port reset timeout to see if the host just needs a
longer timeout.  The USB 3.0 spec says that the warm port reset should
complete within 200ms, or the link state should transition to the
Disconnected state.  Two seconds should be more than enough.

If warm reset fails, try using a different method to get the port out of
Compliance Mode.  Set the port into the Disabled state, and then into
the RxDetect state.  Set the hub->removed_bits so that the USB core will
ignore the port until it sees a device disconnect and reconnect.

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
---
 drivers/usb/core/hub.c      |   69 +++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/host/xhci-hub.c |   22 +++++++++++++-
 2 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 2dd3586..93c8806 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -876,6 +876,61 @@ static int hub_hub_status(struct usb_hub *hub,
 	return ret;
 }
 
+static int hub_set_port_link_state(struct usb_hub *hub, int port1,
+			unsigned int link_status)
+{
+	return set_port_feature(hub->hdev,
+			port1 | (link_status << 3),
+			USB_PORT_FEAT_LINK_STATE);
+}
+
+/*
+ * If USB 3.0 ports are placed into the Disabled state, they will no longer
+ * detect any device connects or disconnects.  This is generally not what the
+ * USB core wants, since it expects a disabled port to produce a port status
+ * change event when a new device connects.
+ *
+ * Instead, set the link state to Disabled, wait for the link to settle into
+ * that state, and then put the port into the RxDetect state.
+ */
+static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
+{
+	int ret;
+	int total_time;
+	u16 portchange, portstatus;
+
+	if (!hub_is_superspeed(hub->hdev))
+		return -EINVAL;
+
+	ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
+	if (ret) {
+		dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
+				port1, ret);
+		return ret;
+	}
+
+	/* Wait for the link to enter the disabled state. */
+	for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
+		ret = hub_port_status(hub, port1, &portstatus, &portchange);
+		if (ret < 0)
+			return ret;
+
+		if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+				USB_SS_PORT_LS_SS_DISABLED)
+			break;
+		if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+			break;
+		msleep(HUB_DEBOUNCE_STEP);
+	}
+	if (total_time >= HUB_DEBOUNCE_TIMEOUT) {
+		dev_warn(hub->intfdev, "Could not disable port %d after %dms\n",
+				port1, total_time);
+		return -ETIMEDOUT;
+	}
+
+	return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
+}
+
 static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 {
 	struct usb_device *hdev = hub->hdev;
@@ -2401,7 +2456,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define HUB_SHORT_RESET_TIME	10
 #define HUB_BH_RESET_TIME	50
 #define HUB_LONG_RESET_TIME	200
-#define HUB_RESET_TIMEOUT	500
+#define HUB_RESET_TIMEOUT	2000
 
 static int hub_port_reset(struct usb_hub *hub, int port1,
 			struct usb_device *udev, unsigned int delay, bool warm);
@@ -4589,11 +4644,21 @@ static void hub_events(void)
 			 * SS.Inactive state.
 			 */
 			if (hub_port_warm_reset_required(hub, portstatus)) {
+				int reset_status;
+
 				dev_dbg(hub_dev, "warm reset port %d\n", i);
-				hub_port_reset(hub, i, NULL,
+				reset_status = hub_port_reset(hub, i, NULL,
 						HUB_BH_RESET_TIME, true);
+				if (reset_status >= 0)
+					goto connect_change;
+
+				dev_dbg(hub_dev, "warm reset failed, disable and re-enable port %d\n",
+						i);
+				reset_status = hub_usb3_port_disable(hub, i);
+				set_bit(i, hub->removed_bits);
 			}
 
+connect_change:
 			if (connect_change)
 				hub_port_connect_change(hub, i,
 						portstatus, portchange);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a686cf4..85315cb 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -761,12 +761,32 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 			break;
 		case USB_PORT_FEAT_LINK_STATE:
 			temp = xhci_readl(xhci, port_array[wIndex]);
+
+			/* Disable port */
+			if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
+				xhci_dbg(xhci, "Disable port %d\n", wIndex);
+				temp = xhci_port_state_to_neutral(temp);
+				xhci_writel(xhci, temp | PORT_PE,
+					port_array[wIndex]);
+				temp = xhci_readl(xhci, port_array[wIndex]);
+				break;
+			}
+
+			/* Put link in RxDetect (enable port) */
+			if (link_state == USB_SS_PORT_LS_RX_DETECT) {
+				xhci_dbg(xhci, "Enable port %d\n", wIndex);
+				xhci_set_link_state(xhci, port_array, wIndex,
+						link_state);
+				temp = xhci_readl(xhci, port_array[wIndex]);
+				break;
+			}
+
 			/* Software should not attempt to set
 			 * port link state above '5' (Rx.Detect) and the port
 			 * must be enabled.
 			 */
 			if ((temp & PORT_PE) == 0 ||
-				(link_state > USB_SS_PORT_LS_RX_DETECT)) {
+				(link_state > USB_SS_PORT_LS_U3)) {
 				xhci_warn(xhci, "Cannot set link state.\n");
 				goto error;
 			}
-- 
1.7.9


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

  Powered by Linux