[PATCH v1] usb: hub: Power cycle root hub if CSC is set during hub_port_reset

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

 



When a FS device is following a suspend-reset-enumeration-data
transfer sequence, sometimes it goes back in suspend just after reset
without the link entering L0. This is seen in only when the following
scenarios are met:
- SOF and EOR happens at the same clock cycle
- UTMI line state should transition from SE0 to K at the same clock
cycle(if the UTMI line state transition from SE0 to J at the same
clock cycle then problem is not seen)

Attemting a power cycle of the root hub recovers the problem described.
To identify the issue, PLS goes to disabled state followed by CSC bit
being set(because of CCS status change).

Signed-off-by: Pratham Pratap <quic_ppratap@xxxxxxxxxxx>
---
v1:
Problem is seen on core emulation setup with eUSB2 PHY test chip.
This failure is seen only in full speed host mode usecase with all
available eUSB2 repeater randomly in 1 out of 5000 to 6000 iterations.
As of now, we don't have any SOC with eUSB2 PHY on which this fix can
be tested.

 drivers/usb/core/hub.c        | 34 ++++++++++++++++++++++++++--------
 drivers/usb/host/xhci-plat.c  |  3 +++
 include/linux/usb/hcd.h       |  1 +
 include/uapi/linux/usb/ch11.h |  1 +
 4 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 47a1c8b..6a65092 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2834,10 +2834,20 @@ static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
 		|| link_state == USB_SS_PORT_LS_COMP_MOD;
 }
 
+static void usb_hub_port_power_cycle(struct usb_device *hdev, struct usb_hub *hub, int port1)
+{
+	dev_info(&hub->ports[port1 - 1]->dev, "attempt power cycle\n");
+	usb_hub_set_port_power(hdev, hub, port1, false);
+	msleep(2 * hub_power_on_good_delay(hub));
+	usb_hub_set_port_power(hdev, hub, port1, true);
+	msleep(hub_power_on_good_delay(hub));
+}
+
 static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 			struct usb_device *udev, unsigned int delay, bool warm)
 {
 	int delay_time, ret;
+	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
 	u16 portstatus;
 	u16 portchange;
 	u32 ext_portstatus = 0;
@@ -2887,8 +2897,21 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 		return -ENOTCONN;
 
 	/* Device went away? */
-	if (!(portstatus & USB_PORT_STAT_CONNECTION))
+	if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+		/*
+		 * When a FS device is following a suspend-reset-enumeration-data_transfer
+		 * sequence, sometimes it goes back in suspend just after reset without the
+		 * link entering L0. To fix this when CSC bit is set(because of CCS status
+		 * change) power cycle the root hub.
+		 */
+		if (udev->reset_resume && (!udev->parent && hcd->fs_suspend_reset) &&
+				(portstatus & USB_PORT_STAT_CSC)) {
+			usb_hub_port_power_cycle(hdev, hub, port1);
+			return -EAGAIN;
+		}
+
 		return -ENOTCONN;
+	}
 
 	/* Retry if connect change is set but status is still connected.
 	 * A USB 3.0 connection may bounce if multiple warm resets were issued,
@@ -5393,13 +5416,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 			break;
 
 		/* When halfway through our retry count, power-cycle the port */
-		if (i == (PORT_INIT_TRIES - 1) / 2) {
-			dev_info(&port_dev->dev, "attempt power cycle\n");
-			usb_hub_set_port_power(hdev, hub, port1, false);
-			msleep(2 * hub_power_on_good_delay(hub));
-			usb_hub_set_port_power(hdev, hub, port1, true);
-			msleep(hub_power_on_good_delay(hub));
-		}
+		if (i == (PORT_INIT_TRIES - 1) / 2)
+			usb_hub_port_power_cycle(hdev, hub, port1);
 	}
 	if (hub->hdev->parent ||
 			!hcd->driver->port_handed_over ||
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index c1edcc9..607c4f0 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -342,6 +342,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
 	hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
 	xhci->shared_hcd->tpl_support = hcd->tpl_support;
 
+	hcd->fs_suspend_reset = of_property_read_bool(sysdev->of_node, "fs-suspend-reset");
+	xhci->shared_hcd->fs_suspend_reset = hcd->fs_suspend_reset;
+
 	if (priv) {
 		ret = xhci_priv_plat_setup(hcd);
 		if (ret)
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 548a028..05ccbc8 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -172,6 +172,7 @@ struct usb_hcd {
 	unsigned		tpl_support:1; /* OTG & EH TPL support */
 	unsigned		cant_recv_wakeups:1;
 			/* wakeup requests from downstream aren't received */
+	unsigned		fs_suspend_reset:1; /* fs suspend reset bug */
 
 	unsigned int		irq;		/* irq allocated */
 	void __iomem		*regs;		/* device memory/io */
diff --git a/include/uapi/linux/usb/ch11.h b/include/uapi/linux/usb/ch11.h
index fb0cd24..576bbf9 100644
--- a/include/uapi/linux/usb/ch11.h
+++ b/include/uapi/linux/usb/ch11.h
@@ -135,6 +135,7 @@ struct usb_port_status {
 #define USB_PORT_STAT_TEST              0x0800
 #define USB_PORT_STAT_INDICATOR         0x1000
 /* bits 13 to 15 are reserved */
+#define USB_PORT_STAT_CSC		0x20000
 
 /*
  * Additions to wPortStatus bit field from USB 3.0
-- 
2.7.4




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

  Powered by Linux