[PATCH v2] usb: ehci-hcd: Add IAA handshake for removing async QH

Ye Li ye.li at nxp.com
Tue Mar 9 04:26:57 CET 2021


According to EHCI spec, software needs to do handshake with HC for
safely removing QH from async list. This handshake is implemented by
setting IAAD (Interrupt on Async Advance Doorbell) bit in USB_USBCMD
register and poll the IAA (Interrupt on Async Advance bit) in the
USB_USBSTS to ensure the HC has released all on-chip state that may
potentially reference one of the data structures just removed.

Current codes only check active status of the last QTD, but this can't
ensure the QH is released from HC. We can meet unrecoverable
"EHCI timed out on TD" errors when running UEFI SCT tests on USB disk.
The USB_ASYNCLISTADDR register is changed to a invalid address when the
issue happens. It is fixed after adding the IAA handshake.

Steps to reproduce the issue:
1. Build the UEFI SCT from https://github.com/tianocore/edk2-test
2. Build the EDK2 UEFI Shell from https://github.com/tianocore/edk2
3. Copy SCT files and Shell.efi to USB disk FAT partition
4. Load the Shell.efi from USB FAT, and run bootefi to execute it
5. After booting into Shell, enter the SCT directory and run "sct -a"
   to execute all tests.
6. Tests run about 1 hour and stop with many EHCI timeout errors like
   EHCI timed out on TD - token=0x801f8c80

Signed-off-by: Ye Li <ye.li at nxp.com>
---
Changes in v2:
 - Remove unnecessary cast and parenthesis
 - Abort the transfer when IAA cycle timeout
 - Add steps to reproduce the issue

 drivers/usb/host/ehci-hcd.c | 27 +++++++++++++++++++++++++++
 drivers/usb/host/ehci.h     |  1 +
 2 files changed, 28 insertions(+)

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 8933f60..ba75c27 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -346,6 +346,28 @@ static int ehci_disable_async(struct ehci_ctrl *ctrl)
 	return ret;
 }
 
+static int ehci_iaa_cycle(struct ehci_ctrl *ctrl)
+{
+	u32 cmd, status;
+	int ret;
+
+	/* Enable Interrupt on Async Advance Doorbell. */
+	cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+	cmd |= CMD_IAAD;
+	ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+
+	ret = handshake(&ctrl->hcor->or_usbsts, STS_IAA, STS_IAA,
+			10 * 1000); /* 10ms timeout */
+	if (ret < 0)
+		printf("EHCI fail timeout STS_IAA set\n");
+
+	status = ehci_readl(&ctrl->hcor->or_usbsts);
+	if (status & STS_IAA)
+		ehci_writel(&ctrl->hcor->or_usbsts, STS_IAA);
+
+	return ret;
+}
+
 static int
 ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 		   int length, struct devrequest *req)
@@ -631,6 +653,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 	flush_dcache_range((unsigned long)&ctrl->qh_list,
 		ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1));
 
+	/* Set IAAD, poll IAA */
+	ret = ehci_iaa_cycle(ctrl);
+	if (ret)
+		goto fail;
+
 	/*
 	 * Invalidate the memory area occupied by buffer
 	 * Don't try to fix the buffer alignment, if it isn't properly
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 8e07554..e9e6f2a5 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -44,6 +44,7 @@ struct ehci_hcor {
 #define STS_ASS		(1 << 15)
 #define	STS_PSS		(1 << 14)
 #define STS_HALT	(1 << 12)
+#define STS_IAA		(1 << 5)
 	uint32_t or_usbintr;
 #define INTR_UE         (1 << 0)                /* USB interrupt enable */
 #define INTR_UEE        (1 << 1)                /* USB error interrupt enable */
-- 
2.7.4



More information about the U-Boot mailing list