[coreboot] New patch to review for coreboot: 309aa8c libpayload: Add timeouts in the OHCI USB driver

Nico Huber (nico.huber@secunet.com) gerrit at coreboot.org
Thu May 31 15:41:32 CEST 2012


Nico Huber (nico.huber at secunet.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1076

-gerrit

commit 309aa8cc0731941ea7b219c8a374bbe182c44891
Author: Nico Huber <nico.huber at secunet.com>
Date:   Mon May 21 14:46:26 2012 +0200

    libpayload: Add timeouts in the OHCI USB driver
    
    We should always have some timeout when we wait for the hardware. This adds
    missing timeouts and a more standard compliant port reset to the OHCI driver.
    
    Change-Id: I2cfcb1039fd12f291e88dcb8b74d41cb5bb2315e
    Signed-off-by: Nico Huber <nico.huber at secunet.com>
---
 payloads/libpayload/drivers/usb/ohci.c    |   22 ++++++++++--
 payloads/libpayload/drivers/usb/ohci_rh.c |   50 +++++++++++++++++++++++------
 2 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c
index 830542e..730c540 100644
--- a/payloads/libpayload/drivers/usb/ohci.c
+++ b/payloads/libpayload/drivers/usb/ohci.c
@@ -195,14 +195,23 @@ dump_td(td_t *cur, int level)
 }
 
 static int
-wait_for_ed(usbdev_t *dev, ed_t *head)
+wait_for_ed(usbdev_t *dev, ed_t *head, int pages)
 {
 	td_t *cur;
 
 	/* wait for results */
+	/* TODO: how long to wait?
+	 *       give 50ms per page plus another 100ms for now
+	 *       this should even work with low-speed
+	 */
+	int timeout = pages*50 + 100;
 	while (((head->head_pointer & ~3) != head->tail_pointer) &&
 		!(head->head_pointer & 1) &&
-		((((td_t*)phys_to_virt(head->head_pointer & ~3))->config & TD_CC_MASK) >= TD_CC_NOACCESS)) {
+		((((td_t*)phys_to_virt(head->head_pointer & ~3))->config
+				& TD_CC_MASK) >= TD_CC_NOACCESS) &&
+		timeout--) {
+		/* don't log every ms */
+		if (!(timeout % 100))
 		debug("intst: %x; ctrl: %x; cmdst: %x; head: %x -> %x, tail: %x, condition: %x\n",
 			OHCI_INST(dev->controller)->opreg->HcInterruptStatus,
 			OHCI_INST(dev->controller)->opreg->HcControl,
@@ -213,6 +222,9 @@ wait_for_ed(usbdev_t *dev, ed_t *head)
 			(((td_t*)phys_to_virt(head->head_pointer & ~3))->config & TD_CC_MASK) >> TD_CC_SHIFT);
 		mdelay(1);
 	}
+	if (timeout < 0)
+		printf("Error: ohci: endpoint "
+			"descriptor processing timed out.\n");
 #if 0
 	/* XXX: The following debugging code may follow invalid lists and
 	 *      cause a reboot.
@@ -341,7 +353,8 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
 	OHCI_INST(dev->controller)->opreg->HcControl |= ControlListEnable;
 	OHCI_INST(dev->controller)->opreg->HcCommandStatus = ControlListFilled;
 
-	int failure = wait_for_ed(dev, head);
+	int failure = wait_for_ed(dev, head,
+			(dalen==0)?0:(last_page - first_page + 1));
 	OHCI_INST(dev->controller)->opreg->HcControl &= ~ControlListEnable;
 
 	/* free memory */
@@ -430,7 +443,8 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
 	OHCI_INST(ep->dev->controller)->opreg->HcControl |= BulkListEnable;
 	OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = BulkListFilled;
 
-	int failure = wait_for_ed(ep->dev, head);
+	int failure = wait_for_ed(ep->dev, head,
+			(dalen==0)?0:(last_page - first_page + 1));
 	OHCI_INST(ep->dev->controller)->opreg->HcControl &= ~BulkListEnable;
 
 	ep->toggle = head->head_pointer & ED_TOGGLE;
diff --git a/payloads/libpayload/drivers/usb/ohci_rh.c b/payloads/libpayload/drivers/usb/ohci_rh.c
index 5cf7ee8..ac92f45 100644
--- a/payloads/libpayload/drivers/usb/ohci_rh.c
+++ b/payloads/libpayload/drivers/usb/ohci_rh.c
@@ -43,15 +43,40 @@ typedef struct {
 static void
 ohci_rh_enable_port (usbdev_t *dev, int port)
 {
-	if (!(OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & CurrentConnectStatus))
-		return;
-
-	OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = SetPortEnable; // enable port
-	mdelay(10);
-	while (!(OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] & PortEnableStatus)) mdelay(1);
-	OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = SetPortReset; // reset port
-	while (OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] & PortResetStatus) mdelay(1);
-	OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = PortResetStatusChange;
+	/* Reset RH port should hold 50ms with pulses of at least 10ms and
+	 * gaps of at most 3ms (usb20 spec 7.1.7.5).
+	 * After reset, the port will be enabled automatically (ohci spec
+	 * 7.4.4).
+	 */
+	int delay = 100; /* 100 * 500us == 50ms */
+	while (delay > 0) {
+		if (!(OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port]
+					& CurrentConnectStatus))
+			return;
+
+		/* start reset */
+		OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] =
+			SetPortReset;
+		int timeout = 200; /* timeout after 200 * 500us == 100ms */
+		while ((OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port]
+					& PortResetStatus)
+				&& timeout--) {
+			udelay(500); delay--;
+		}
+		if (OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port]
+				& PortResetStatus) {
+			debug("Warning: root-hub port reset timed out.\n");
+			break;
+		}
+		if ((200-timeout) < 20)
+			debug("Warning: port reset too short: %dms; "
+					"should be at least 10ms.\n",
+					(200-timeout)/2);
+		/* clear reset status change */
+		OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] =
+			PortResetStatusChange;
+		debug ("rh port reset finished after %dms.\n", (200-timeout)/2);
+	}
 }
 
 /* disable root hub */
@@ -59,7 +84,12 @@ static void
 ohci_rh_disable_port (usbdev_t *dev, int port)
 {
 	OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = ClearPortEnable; // disable port
-	while (OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] & PortEnableStatus) mdelay(1);
+	int timeout = 50; /* timeout after 50 * 100us == 5ms */
+	while ((OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port]
+				& PortEnableStatus)
+			&& timeout--) {
+		udelay(100);
+	}
 }
 
 static void




More information about the coreboot mailing list