Duncan Laurie merged this change.

View Change

Approvals: build bot (Jenkins): Verified Julius Werner: Looks good to me, approved
libpayload/usb: Increase USB request timeout to 5 s

Increase the timeout for USB requests to 5 seconds for all USB host
controllers.

Prior to this fix, the xCHI driver was detecting false timeouts during
SET ADDRESS requests when nested downstream hubs were connected to the
xHCI root hub.

BUG=b:124730179
BRANCH=sarien
TEST=Build libpayload and depthcharge on sarien/arcada.
TEST=Without change replicate USB set address timeouts in depthcharge
when dock and 4K monitor connected (which includes a total of 4 USB
hubs). With timeout fix, depthcharge boots OS with no USB errors and
the same USB topology. Note that this tests xHCI operation only.

Change-Id: I53e3e67d893420e7c9e8b52c47dd0edb979e5468
Signed-off-by: Keith Short <keithshort@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/33671
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
---
M payloads/libpayload/drivers/usb/dwc2.c
M payloads/libpayload/drivers/usb/ehci.c
M payloads/libpayload/drivers/usb/ohci.c
M payloads/libpayload/drivers/usb/uhci.c
M payloads/libpayload/drivers/usb/xhci_events.c
M payloads/libpayload/include/usb/usb.h
6 files changed, 47 insertions(+), 35 deletions(-)

diff --git a/payloads/libpayload/drivers/usb/dwc2.c b/payloads/libpayload/drivers/usb/dwc2.c
index 34de3bc..06c54e1 100644
--- a/payloads/libpayload/drivers/usb/dwc2.c
+++ b/payloads/libpayload/drivers/usb/dwc2.c
@@ -146,6 +146,8 @@
return !(hprt.prtena && hprt.prtconnsts);
}

+#define DWC2_SLEEP_TIME_US 5
+
/*
* This function returns the actual transfer length when the transfer succeeded
* or an error code if the transfer failed
@@ -157,14 +159,14 @@
hcchar_t hcchar;
hctsiz_t hctsiz;
dwc2_reg_t *reg = DWC2_REG(ep->dev->controller);
- int timeout = 600000; /* time out after 600000 * 5us == 3s */
+ int timeout = USB_MAX_PROCESSING_TIME_US / DWC_SLEEP_TIME_US;

/*
* TODO: We should take care of up to three times of transfer error
* retry here, according to the USB 2.0 spec 4.5.2
*/
do {
- udelay(5);
+ udelay(DWC_SLEEP_TIME_US);
hcint.d32 = readl(&reg->host.hchn[ch_num].hcintn);
hctsiz.d32 = readl(&reg->host.hchn[ch_num].hctsizn);

@@ -374,12 +376,15 @@
return 1;
}

+#define USB_FULL_LOW_SPEED_FRAME_US 1000
+
static int
dwc2_transfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, uint32_t ch_num,
u8 *src, uint8_t skip_nak)
{
split_info_t split;
- int ret, short_pkt, transferred = 0, timeout = 3000;
+ int ret, short_pkt, transferred = 0;
+ int timeout = USB_MAX_PROCESSING_TIME_US / USB_FULL_LOW_SPEED_FRAME_US;

ep->toggle = pid;

@@ -393,11 +398,11 @@

/*
* dwc2_split_transfer() waits for the next FullSpeed
- * frame boundary, so we have one try per millisecond.
- * It's 3s timeout for each split transfer.
+ * frame boundary, so we only need to delay 500 us
+ * here to have one try per millisecond.
*/
if (ret == -HCSTAT_NAK && !skip_nak && --timeout) {
- udelay(500);
+ udelay(USB_FULL_LOW_SPEED_FRAME_US / 2);
goto nak_retry;
}
} else {
diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c
index 3d6c077..6876340 100644
--- a/payloads/libpayload/drivers/usb/ehci.c
+++ b/payloads/libpayload/drivers/usb/ehci.c
@@ -247,6 +247,8 @@
free((void *)qh);
}

+#define EHCI_SLEEP_TIME_US 50
+
static int wait_for_tds(qtd_t *head)
{
/* returns the amount of bytes *not* transmitted, or -1 for error */
@@ -256,18 +258,10 @@
if (0) dump_td(virt_to_phys(cur));

/* wait for results */
- /* how long to wait?
- * tested with some USB2.0 flash sticks:
- * TUR turn around took
- * about 2.2s for the slowest (13fe:3800)
- * max. 250ms for the others
- * slowest non-TUR turn around took about 1.3s
- * set to 3s to be safe as a failed TUR can be fatal
- */
- int timeout = 60000; /* time out after 60000 * 50us == 3s */
+ int timeout = USB_MAX_PROCESSING_TIME_US / EHCI_SLEEP_TIME_US;
while ((cur->token & QTD_ACTIVE) && !(cur->token & QTD_HALTED)
&& timeout--)
- udelay(50);
+ udelay(EHCI_SLEEP_TIME_US);
if (timeout < 0) {
usb_debug("Error: ehci: queue transfer "
"processing timed out.\n");
diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c
index ecd1084..f1dc081 100644
--- a/payloads/libpayload/drivers/usb/ohci.c
+++ b/payloads/libpayload/drivers/usb/ohci.c
@@ -292,14 +292,13 @@
OHCI_INST (controller)->opreg->HcControl &= ~PeriodicListEnable;
}

+#define OHCI_SLEEP_TIME_US 1000
+
static int
wait_for_ed(usbdev_t *dev, ed_t *head, int pages)
{
/* wait for results */
- /* TOTEST: how long to wait?
- * give 2s per TD (2 pages) plus another 2s for now
- */
- int timeout = pages*1000 + 2000;
+ int timeout = USB_MAX_PROCESSING_TIME_US / OHCI_SLEEP_TIME_US;
while (((head->head_pointer & ~3) != head->tail_pointer) &&
!(head->head_pointer & 1) &&
((((td_t*)phys_to_virt(head->head_pointer & ~3))->config
@@ -315,9 +314,9 @@
((td_t*)phys_to_virt(head->head_pointer & ~3))->next_td,
head->tail_pointer,
(((td_t*)phys_to_virt(head->head_pointer & ~3))->config & TD_CC_MASK) >> TD_CC_SHIFT);
- mdelay(1);
+ udelay(OHCI_SLEEP_TIME_US);
}
- if (timeout < 0)
+ if (timeout <= 0)
usb_debug("Error: ohci: endpoint "
"descriptor processing timed out.\n");
/* Clear the done queue. */
diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c
index e62fd25..32d4090 100644
--- a/payloads/libpayload/drivers/usb/uhci.c
+++ b/payloads/libpayload/drivers/usb/uhci.c
@@ -272,21 +272,23 @@
uhci_reg_read16(controller, USBCMD) & ~1); // stop work on schedule
}

+#define UHCI_SLEEP_TIME_US 30
+#define UHCI_TIMEOUT (USB_MAX_PROCESSING_TIME_US / UHCI_SLEEP_TIME_US)
#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))

static td_t *
wait_for_completed_qh (hci_t *controller, qh_t *qh)
{
- int timeout = 1000; /* max 30 ms. */
+ int timeout = UHCI_TIMEOUT;
void *current = GET_TD (qh->elementlinkptr);
while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) {
if (current != GET_TD (qh->elementlinkptr)) {
current = GET_TD (qh->elementlinkptr);
- timeout = 1000;
+ timeout = UHCI_TIMEOUT;
}
uhci_reg_write16(controller, USBSTS,
uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers
- udelay (30);
+ udelay(UHCI_SLEEP_TIME_US);
}
return (GET_TD (qh->elementlinkptr) ==
0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr));
diff --git a/payloads/libpayload/drivers/usb/xhci_events.c b/payloads/libpayload/drivers/usb/xhci_events.c
index dacb5d86..c21797d 100644
--- a/payloads/libpayload/drivers/usb/xhci_events.c
+++ b/payloads/libpayload/drivers/usb/xhci_events.c
@@ -236,7 +236,7 @@
* we don't get a response after 5s. Still, let the caller decide,
* what to do then.
*/
- unsigned long timeout_us = 5 * 1000 * 1000; /* 5s */
+ unsigned long timeout_us = USB_MAX_PROCESSING_TIME_US; /* 5s */
int cc = TIMEOUT;
/*
* Expects two command completion events:
@@ -280,13 +280,7 @@
const trb_t *const address,
const int clear_event)
{
- /*
- * The Address Device Command should take most time, as it has to
- * communicate with the USB device. Set address processing shouldn't
- * take longer than 50ms (at the slave). Let's take a timeout of
- * 100ms.
- */
- unsigned long timeout_us = 100 * 1000; /* 100ms */
+ unsigned long timeout_us = USB_MAX_PROCESSING_TIME_US; /* 5s */
int cc = TIMEOUT;
while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) {
if ((xhci->er.cur->ptr_low == virt_to_phys(address)) &&
@@ -311,8 +305,8 @@
xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id)
{
xhci_spew("Waiting for transfer on ID %d EP %d\n", slot_id, ep_id);
- /* 3s for all types of transfers */ /* TODO: test, wait longer? */
- unsigned long timeout_us = 3 * 1000 * 1000;
+ /* 5s for all types of transfers */
+ unsigned long timeout_us = USB_MAX_PROCESSING_TIME_US;
int ret = TIMEOUT;
while (xhci_wait_for_event_type(xhci, TRB_EV_TRANSFER, &timeout_us)) {
if (TRB_GET(ID, xhci->er.cur) == slot_id &&
diff --git a/payloads/libpayload/include/usb/usb.h b/payloads/libpayload/include/usb/usb.h
index 79c4586..db7ec57 100644
--- a/payloads/libpayload/include/usb/usb.h
+++ b/payloads/libpayload/include/usb/usb.h
@@ -71,6 +71,24 @@
/* SetAddress() recovery interval (USB 2.0 specification 9.2.6.3 */
#define SET_ADDRESS_MDELAY 2

+/*
+ * USB sets an upper limit of 5 seconds for any transfer to be completed.
+ *
+ * Data originally from EHCI driver:
+ * Tested with some USB2.0 flash sticks:
+ * TUR turn around took about 2.2s for the slowest (13fe:3800), maximum
+ * of 250ms for the others.
+ *
+ * SET ADDRESS on xHCI controllers.
+ * The USB specification indicates that devices must complete processing
+ * of a SET ADDRESS request within 50 ms. However, some hubs were found
+ * to take more than 100 ms to complete a SET ADDRESS request on a
+ * downstream port.
+ */
+#define USB_MAX_PROCESSING_TIME_US (5 * 1000 * 1000)
+
+#define USB_FULL_LOW_SPEED_FRAME_US 1000
+
typedef struct {
unsigned char bDescLength;
unsigned char bDescriptorType;

To view, visit change 33671. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I53e3e67d893420e7c9e8b52c47dd0edb979e5468
Gerrit-Change-Number: 33671
Gerrit-PatchSet: 6
Gerrit-Owner: Keith Short <keithshort@chromium.org>
Gerrit-Reviewer: Duncan Laurie <dlaurie@chromium.org>
Gerrit-Reviewer: EricR Lai <ericr_lai@compal.corp-partner.google.com>
Gerrit-Reviewer: Julius Werner <jwerner@chromium.org>
Gerrit-Reviewer: Keith Short <keithshort@chromium.org>
Gerrit-Reviewer: Lance Zhao <lance.zhao@gmail.com>
Gerrit-Reviewer: Patrick Georgi <pgeorgi@google.com>
Gerrit-Reviewer: Paul Menzel <paulepanter@users.sourceforge.net>
Gerrit-Reviewer: build bot (Jenkins) <no-reply@coreboot.org>
Gerrit-MessageType: merged