Linux-USB Archive on lore.kernel.org
 help / color / Atom feed
From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
To: Felipe Balbi <balbi@kernel.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Thinh.Nguyen@synopsys.com, linux-usb@vger.kernel.org,
	Mathias Nyman <mathias.nyman@intel.com>
Cc: John Youn <John.Youn@synopsys.com>
Subject: [PATCH v2 6/7] usb: xhci: Workaround lost disconnect port status
Date: Fri, 09 Apr 2021 17:47:20 -0700
Message-ID: <a90cfd8e3008c3664a916a12dc3c0a79a520c0ab.1618014279.git.Thinh.Nguyen@synopsys.com> (raw)
In-Reply-To: <cover.1618014279.git.Thinh.Nguyen@synopsys.com>

If an eSS device with active periodic transfers is disconnected from the
DWC_usb31 (v1.90a and prior) host controller at root port, the host
controller may not detect a disconnection. By active transfers, it
means that the endpoint is not in flow control, and there are active
Transfer Descriptors available for the host to initiate transfers to the
endpoint. This issue can occur if the endpoint periodic interval is in
2ms, 4ms, or 8ms.

In addition, the host controller will not be able to detect a new device
connection while the disconnection is not processed. The controller will
set the link state of the affected port to eSS_INACTIVE.

To workaround this, have the xHCI driver polls for the eSS root port
status every 2 seconds. If eSS_INACTIVE state is detected, initiate a
fake connection change to stop all the active endpoints and start
polling for new connection change.

Since XHCI_COMP_MODE_QUIRK is basically doing the same thing except for
polling for a different link state, we will use the same timer and
polling rate for this new workaround. Introduce a new quirk
XHCI_LOST_DISCONNECT_QUIRK to poll for eSS_INACTIVE port link state and
fake a connection change.

Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
---
 Changes in v2:
 - None

 drivers/usb/host/xhci-hub.c     | 10 +++++++-
 drivers/usb/host/xhci.c         | 44 ++++++++++++++++++++++++---------
 include/linux/usb/xhci-quirks.h |  1 +
 3 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 8bfafbd680ab..32fbf95f021b 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -868,6 +868,11 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
 		if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
 				(pls == USB_SS_PORT_LS_COMP_MOD))
 			pls |= USB_PORT_STAT_CONNECTION;
+
+		/* Fake a connection change */
+		if ((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) &&
+				pls == XDEV_INACTIVE)
+			pls |= USB_PORT_STAT_CONNECTION;
 	}
 
 	/* update status field */
@@ -887,7 +892,8 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
 	u32 all_ports_seen_u0 = ((1 << xhci->usb3_rhub.num_ports) - 1);
 	bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
 
-	if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
+	if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK) ||
+	    (xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK))
 		return;
 
 	if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
@@ -1654,6 +1660,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
 		trace_xhci_hub_status_data(i, temp);
 
 		if ((temp & mask) != 0 ||
+			((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) &&
+				(temp & PORT_PLS_MASK) == XDEV_INACTIVE) ||
 			(bus_state->port_c_suspend & 1 << i) ||
 			(bus_state->resume_done[i] && time_after_eq(
 			    jiffies, bus_state->resume_done[i]))) {
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index e1b3d1063f6b..62275ee88849 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -485,10 +485,14 @@ static void port_check(struct timer_list *t)
 
 	xhci = from_timer(xhci, t, port_check_timer);
 	rhub = &xhci->usb3_rhub;
+	hcd = xhci->shared_hcd;
 
 	for (i = 0; i < rhub->num_ports; i++) {
+		bool poll_rhub = false;
+
 		temp = readl(rhub->ports[i]->addr);
-		if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
+		if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+		    ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD)) {
 			/*
 			 * Compliance Mode Detected. Letting USB Core
 			 * handle the Warm Reset
@@ -498,8 +502,15 @@ static void port_check(struct timer_list *t)
 					i + 1);
 			xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
 					"Attempting compliance mode recovery");
-			hcd = xhci->shared_hcd;
 
+			poll_rhub = true;
+		}
+
+		if ((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) &&
+		    ((temp & PORT_PLS_MASK) == XDEV_INACTIVE))
+			poll_rhub = true;
+
+		if (poll_rhub) {
 			if (hcd->state == HC_STATE_SUSPENDED)
 				usb_hcd_resume_root_hub(hcd);
 
@@ -507,7 +518,9 @@ static void port_check(struct timer_list *t)
 		}
 	}
 
-	if (xhci->port_status_u0 != ((1 << rhub->num_ports) - 1))
+	if ((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) ||
+	    ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+	     xhci->port_status_u0 != ((1 << rhub->num_ports) - 1)))
 		mod_timer(&xhci->port_check_timer,
 			jiffies + msecs_to_jiffies(PORT_CHECK_MSECS));
 }
@@ -593,10 +606,12 @@ static int xhci_init(struct usb_hcd *hcd)
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init");
 
 	/* Initializing Compliance Mode Recovery Data If Needed */
-	if (xhci_compliance_mode_recovery_timer_quirk_check()) {
+	if (xhci_compliance_mode_recovery_timer_quirk_check())
 		xhci->quirks |= XHCI_COMP_MODE_QUIRK;
+
+	if (xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK ||
+	    xhci->quirks & XHCI_COMP_MODE_QUIRK)
 		port_check_timer_init(xhci);
-	}
 
 	return retval;
 }
@@ -736,8 +751,9 @@ static void xhci_stop(struct usb_hcd *hcd)
 	xhci_cleanup_msix(xhci);
 
 	/* Deleting Compliance Mode Recovery Timer */
-	if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
-			(!(xhci_all_ports_seen_u0(xhci)))) {
+	if ((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) ||
+	    ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+	     !(xhci_all_ports_seen_u0(xhci)))) {
 		del_timer_sync(&xhci->port_check_timer);
 		xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
 				"%s: port check timer deleted", __func__);
@@ -1058,8 +1074,9 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
 	 * Deleting Port Check Timer because the xHCI Host
 	 * is about to be suspended.
 	 */
-	if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
-			(!(xhci_all_ports_seen_u0(xhci)))) {
+	if ((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) ||
+	    ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+	     !(xhci_all_ports_seen_u0(xhci)))) {
 		del_timer_sync(&xhci->port_check_timer);
 		xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
 				"%s: port check timer deleted", __func__);
@@ -1145,8 +1162,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 	/* If restore operation fails, re-initialize the HC during resume */
 	if ((temp & STS_SRE) || hibernated) {
 
-		if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
-				!(xhci_all_ports_seen_u0(xhci))) {
+		if ((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) ||
+		    ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+		     !(xhci_all_ports_seen_u0(xhci)))) {
 			del_timer_sync(&xhci->port_check_timer);
 			xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
 				"Port Check Timer deleted!");
@@ -1247,7 +1265,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 	 * to suffer the Compliance Mode issue again. It doesn't matter if
 	 * ports have entered previously to U0 before system's suspension.
 	 */
-	if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running)
+	if (!comp_timer_running &&
+	    ((xhci->quirks & XHCI_LOST_DISCONNECT_QUIRK) ||
+	     (xhci->quirks & XHCI_COMP_MODE_QUIRK)))
 		port_check_timer_init(xhci);
 
 	if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL)
diff --git a/include/linux/usb/xhci-quirks.h b/include/linux/usb/xhci-quirks.h
index 5bedf5a25f7a..df92e78b8083 100644
--- a/include/linux/usb/xhci-quirks.h
+++ b/include/linux/usb/xhci-quirks.h
@@ -60,5 +60,6 @@
 #define XHCI_NO_SOFT_RETRY		BIT_ULL(40)
 #define XHCI_ISOC_BLOCKED_DISCONNECT	BIT_ULL(41)
 #define XHCI_LIMIT_FS_BI_INTR_EP	BIT_ULL(42)
+#define XHCI_LOST_DISCONNECT_QUIRK	BIT_ULL(43)
 
 #endif /* __LINUX_USB_XHCI_QUIRKS_H */
-- 
2.28.0


  parent reply index

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-09  1:41 [PATCH 0/6] usb: Set quirks for xhci/dwc3 host mode Thinh Nguyen
2021-04-09  1:41 ` [PATCH 1/6] usb: xhci: Move quirks definitions to common usb header Thinh Nguyen
2021-04-09  6:50   ` Greg Kroah-Hartman
2021-04-09  8:01     ` Thinh Nguyen
2021-04-09 16:26   ` kernel test robot
2021-04-09 19:54   ` kernel test robot
2021-04-09  1:42 ` [PATCH 2/6] usb: xhci: Check for blocked disconnection Thinh Nguyen
2021-04-09  1:42 ` [PATCH 3/6] usb: xhci: Workaround undercalculated BW for fullspeed BI Thinh Nguyen
2021-04-09  1:42 ` [PATCH 4/6] usb: xhci: Rename Compliance mode timer quirk Thinh Nguyen
2021-04-09  1:42 ` [PATCH 5/6] usb: xhci: Workaround lost disconnect port status Thinh Nguyen
2021-04-09  1:42 ` [PATCH 6/6] usb: dwc3: host: Set quirks base on version Thinh Nguyen
2021-04-09  6:53   ` Greg Kroah-Hartman
2021-04-09  8:01     ` Thinh Nguyen
2021-04-09 13:22       ` Greg Kroah-Hartman
2021-04-10  0:44         ` Thinh Nguyen
2021-04-10  0:46 ` [PATCH v2 0/7] usb: Set quirks for xhci/dwc3 host mode Thinh Nguyen
2021-04-10  0:46   ` [PATCH v2 1/7] usb: xhci: Move quirks definitions to common usb header Thinh Nguyen
2021-04-10  0:46   ` [PATCH v2 2/7] usb: xhci: Move xhci-plat header " Thinh Nguyen
2021-04-10  0:47   ` [PATCH v2 3/7] usb: xhci: Check for blocked disconnection Thinh Nguyen
2021-04-27 13:08     ` Mathias Nyman
2021-04-27 22:30       ` Thinh Nguyen
2021-04-28 13:32         ` Mathias Nyman
2021-04-29  0:54           ` Thinh Nguyen
2021-04-10  0:47   ` [PATCH v2 4/7] usb: xhci: Workaround undercalculated BW for fullspeed BI Thinh Nguyen
2021-04-28 11:57     ` Mathias Nyman
2021-04-10  0:47   ` [PATCH v2 5/7] usb: xhci: Rename Compliance mode timer quirk Thinh Nguyen
2021-04-10  0:47   ` Thinh Nguyen [this message]
2021-04-28 13:48     ` [PATCH v2 6/7] usb: xhci: Workaround lost disconnect port status Mathias Nyman
2021-04-29  1:00       ` Thinh Nguyen
2021-04-10  0:47   ` [PATCH v2 7/7] usb: dwc3: host: Set quirks base on version Thinh Nguyen
2021-04-19 21:07   ` [PATCH v2 0/7] usb: Set quirks for xhci/dwc3 host mode Thinh Nguyen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a90cfd8e3008c3664a916a12dc3c0a79a520c0ab.1618014279.git.Thinh.Nguyen@synopsys.com \
    --to=thinh.nguyen@synopsys.com \
    --cc=John.Youn@synopsys.com \
    --cc=balbi@kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mathias.nyman@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Linux-USB Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-usb/0 linux-usb/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-usb linux-usb/ https://lore.kernel.org/linux-usb \
		linux-usb@vger.kernel.org
	public-inbox-index linux-usb

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-usb


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git