libpayload: usb: Add interval attribute to endpoints

Read bInterval from endpoint descriptors and store it in our endpoint_t
struct. The interval is encoded dependently on the device' speed and the
endpoint's type. Therefore, it will be normalized to the binary logarithm
of the number of microframes, i.e.
  t = 125us * 2^interval

The interval attribute will be used in the xHCI driver.

Original-Change-Id: I65a8eda6145faf34666800789f0292e640a8141b
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: http://review.coreboot.org/3449
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
(cherry picked from commit aee44fa37d780cfec95c444f43defd89ded021f0)

BUG=chrome-os-partner:21969
TEST=None

Change-Id: Ic42ad3c193390d5838b563346604b1ef9f385b52
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/168094
diff --git a/payloads/libpayload/drivers/usb/usb.c b/payloads/libpayload/drivers/usb/usb.c
index a7ed4c2..437a62d 100644
--- a/payloads/libpayload/drivers/usb/usb.c
+++ b/payloads/libpayload/drivers/usb/usb.c
@@ -279,6 +279,48 @@
 	return adr;
 }
 
+/* Normalize bInterval to log2 of microframes */
+static int
+usb_decode_interval(const int speed, const endpoint_type type, const unsigned char bInterval)
+{
+#define LOG2(a) ((sizeof(unsigned) << 3) - __builtin_clz(a) - 1)
+	switch (speed) {
+	case LOW_SPEED:
+		switch (type) {
+		case ISOCHRONOUS: case INTERRUPT:
+			return LOG2(bInterval) + 3;
+		default:
+			return 0;
+		}
+	case FULL_SPEED:
+		switch (type) {
+		case ISOCHRONOUS:
+			return (bInterval - 1) + 3;
+		case INTERRUPT:
+			return LOG2(bInterval) + 3;
+		default:
+			return 0;
+		}
+	case HIGH_SPEED:
+		switch (type) {
+		case ISOCHRONOUS: case INTERRUPT:
+			return bInterval - 1;
+		default:
+			return LOG2(bInterval);
+		}
+	case SUPER_SPEED:
+		switch (type) {
+		case ISOCHRONOUS: case INTERRUPT:
+			return bInterval - 1;
+		default:
+			return 0;
+		}
+	default:
+		return 0;
+	}
+#undef LOG2
+}
+
 static int
 set_address (hci_t *controller, int speed, int hubport, int hubaddr)
 {
@@ -358,6 +400,7 @@
 			dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
 			dev->endpoints[0].direction = SETUP;
 			dev->endpoints[0].type = CONTROL;
+			dev->endpoints[0].interval = usb_decode_interval(dev->speed, CONTROL, endp->bInterval);
 			for (j = 1; j <= current->bNumEndpoints; j++) {
 #ifdef USB_DEBUG
 				static const char *transfertypes[4] = {
@@ -375,6 +418,7 @@
 					((endp->bEndpointAddress & 0x80) ==
 					 0) ? OUT : IN;
 				ep->type = endp->bmAttributes;
+				ep->interval = usb_decode_interval(dev->speed, ep->type, endp->bInterval);
 				endp = (endpoint_descriptor_t
 					*) (((char *) endp) + endp->bLength);
 			}
diff --git a/payloads/libpayload/include/usb/usb.h b/payloads/libpayload/include/usb/usb.h
index 0296577..a351690 100644
--- a/payloads/libpayload/include/usb/usb.h
+++ b/payloads/libpayload/include/usb/usb.h
@@ -91,6 +91,8 @@
 	int toggle;
 	int maxpacketsize;
 	endpoint_type type;
+	int interval; /* expressed as binary logarithm of the number
+			 of microframes (i.e. t = 125us * 2^interval) */
 } endpoint_t;
 
 enum { FULL_SPEED = 0, LOW_SPEED = 1, HIGH_SPEED = 2, SUPER_SPEED = 3 };