libpayload: Handle underruns in UHCI interrupt queues

If usb_poll() isn't called fast enough, the UHCI controller marks an
underrun interrupt queue as done (terminating the queue at the head).
We can recover from this situation, when usb_poll() gets called again,
and the queue is processed.

Change-Id: Id56c9df44d6dbd53cd30ad89dfb5bf5977799829
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: http://review.coreboot.org/1898
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c
index bfa53f6..386392b 100644
--- a/payloads/libpayload/drivers/usb/uhci.c
+++ b/payloads/libpayload/drivers/usb/uhci.c
@@ -590,6 +590,11 @@
 		q->lastread = (q->lastread + 1) % q->total;
 		return &q->data[current*q->reqsize];
 	}
+	/* reset queue if we fully processed it after underrun */
+	else if (q->qh->elementlinkptr & FLISTP_TERMINATE) {
+		usb_debug("resetting underrun uhci interrupt queue.\n");
+		q->qh->elementlinkptr = virt_to_phys(q->tds + q->lastread);
+	}
 	return NULL;
 }