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@secunet.com>
Reviewed-on: http://review.coreboot.org/1076
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Nico Huber 2012-05-21 14:46:26 +02:00 committed by Stefan Reinauer
parent 5e3428ea20
commit afe86c0b74
2 changed files with 57 additions and 13 deletions

View File

@ -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;

View File

@ -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;
/* 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;
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;
/* 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