[PATCH v4 3/3] usb: host: Implement workaround for Erratum A-009668

From: yinbo.zhu
Date: Tue Dec 19 2017 - 05:35:57 EST

From: yinbo.zhu <yinbo.zhu@xxxxxxx>

This issue is observed in USB 2.0 mode when the USB 3.0 host
controller is connected to a FS/LS device via a hub. The
host controller issues start-split (SSPLIT) and (CSPLIT)
tokens to accomplish a split-transaction. A
split-transaction consists of a SSPLIT token, token/data
consists of a SSPLIT token, token/data packets, CSPLIT token
and token/data/handshake packets. A SSPLIT token is issued
by the host controller to the hub followed by token/data/
handshake packets. The hub then relays the token/data/
handshake packets to the FS /LS device. Sometime later, the
host controller issues a CSPLIT token followed by the same
token/data/handshake packets to the hub to complete the
split-transaction. As an example scenario, when the xHCI
driver issues an Address device command with BSR=0, the
host controller sends SETUP(SET_ADDRESS) tokens on the USB
as part of splittransactions. If the host controller
receives a NYET response from the hub for the CSPLIT SETUP,
it means that the split-transaction has not yet been
completed or the hub is not able to handle the split
transaction. In such a case, the host controller keeps
retrying the splittransactions until such time an ACK
response is received from the hub for the CSPLIT SETUP token
. If the split-transactions do not complete in a time bound
manner, the xHCI driver may issue a Stop Endpoint Command.
The host controller does not service the Stop Endpoint
Command and eventually the xHCI driver times out waiting for
the Stop Endpoint Command to complete.

Impact: Stop Endpoint Command does not complete.
Workaround: Instead of issuing a Stop Endpoint Command,
issue a Disable Slot Command with the corresponding slot
ID. Alternately, you can issue an Address Device Command
with BSR=1.

Configs Affected:
LS1012A-R1.0, LS1012A-R2.0, LS1021-20-22A-R1.0,
LS1021-20-22A-R2.0, LS1043-23A-R1.0, LS1043-23A-R1.1,
LS1046-26A-R1.0, LS1088-48A-R1.0, LS2080-40A-R1.0,
LS2081A-R1.1, LS2085-45A-R1.0, LS2088-48A-R1.0,
LS2088-48A-R1.1, LX2160-2120-2080A-R1.0.

Signed-off-by: yinbo zhu <yinbo.zhu@xxxxxxx>
Change in v4:
Remove the point in "yinbo.zhu"
drivers/usb/dwc3/core.c | 2 ++
drivers/usb/dwc3/core.h | 2 ++
drivers/usb/dwc3/host.c | 3 +++
drivers/usb/host/xhci-plat.c | 3 +++
drivers/usb/host/xhci.c | 12 ++++++++++++
drivers/usb/host/xhci.h | 1 +
6 files changed, 23 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 863f2c0..90b097c 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1059,6 +1059,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->quirk_stop_transfer_in_block = device_property_read_bool(dev,
+ dwc->quirk_stop_ep_in_u1 = device_property_read_bool(dev,
+ "snps,quirk_stop_ep_in_u1");
device_property_read_u8(dev, "snps,tx_de_emphasis",
device_property_read_string(dev, "snps,hsphy_interface",
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6276678..b55a443 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -860,6 +860,7 @@ struct dwc3_scratchpad_array {
* @quirk_reverse_in_out: prevent tx fifo reverse the data direction
* @quirk_stop_transfer_in_block: prevent block transmission from being
* interrupted
+ * @quirk_stop_ep_in_u1: replace stop commad with disable slot command
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
@@ -1014,6 +1015,7 @@ struct dwc3 {
unsigned tx_de_emphasis:2;
unsigned quirk_reverse_in_out:1;
unsigned quirk_stop_transfer_in_block:1;
+ unsigned quirk_stop_ep_in_u1:1;

u16 imod_interval;
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 78cb7bb..0a34274 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -96,6 +96,9 @@ int dwc3_host_init(struct dwc3 *dwc)
if (dwc->quirk_stop_transfer_in_block)
props[prop_idx++].name = "quirk-stop-transfer-in-block";

+ if (dwc->quirk_stop_ep_in_u1)
+ props[prop_idx++].name = "quirk-stop-ep-in-u1";
if (dwc->usb3_lpm_capable)
props[prop_idx++].name = "usb3-lpm-capable";

diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 35e0fc8..49d6cb4 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -273,6 +273,9 @@ static int xhci_plat_probe(struct platform_device *pdev)

+ if (device_property_read_bool(&pdev->dev, "quirk-stop-ep-in-u1"))
+ xhci->quirks |= XHCI_STOP_EP_IN_U1;
if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
xhci->quirks |= XHCI_BROKEN_PORT_PED;

diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 5141856..20c9af4 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1521,6 +1521,18 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)

+ /*
+ *erratum A-009668: Stop Endpoint Command does not complete.
+ *Workaround: Instead of issuing a Stop Endpoint Command,
+ *issue a Disable Slot Command with the corresponding slot ID.
+ *Alternately, you can issue an Address Device Command with
+ *BSR=1
+ */
+ if ((urb->dev->speed <= USB_SPEED_HIGH) &&
+ (xhci->quirks & XHCI_STOP_EP_IN_U1)) {
+ xhci_queue_slot_control(xhci, command, TRB_DISABLE_SLOT,
+ urb->dev->slot_id);
+ }
spin_unlock_irqrestore(&xhci->lock, flags);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index db10ee4..22ba752 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1821,6 +1821,7 @@ struct xhci_hcd {
#define XHCI_MISSING_CAS (1 << 24)
+#define XHCI_STOP_EP_IN_U1 BIT(34)
/* For controller with a broken Port Disable implementation */
#define XHCI_BROKEN_PORT_PED (1 << 25)