From afe86c0b74cc7f5dcaefc34155daf299e8377353 Mon Sep 17 00:00:00 2001 From: Nico Huber Date: Mon, 21 May 2012 14:46:26 +0200 Subject: 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 Reviewed-on: http://review.coreboot.org/1076 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- payloads/libpayload/drivers/usb/ohci_rh.c | 50 ++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'payloads/libpayload/drivers/usb/ohci_rh.c') diff --git a/payloads/libpayload/drivers/usb/ohci_rh.c b/payloads/libpayload/drivers/usb/ohci_rh.c index 5cf7ee8376..ac92f45a46 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 -- cgit v1.2.3