libpayload: Redirect USB slave init through controller driver

xHCI requires special treatment of set_address since it determines
the device number itself (instead of the driver, as with the other
controllers). The controller also wants to validate a chosen device
configuration and we need to setup additional structures for the
device and the endpoints.

Therefore, we add three functions to the hci_t structure, namely:
  set_address()
  finish_device_config()
  destroy_device()
Current implementation for the Set Address request moved into
generic_set_address() which is set_address() for the UHCI, OCHI and
EHCI drivers. The latter two are only provided as hooks for the xHCI
driver.

The Set Configuration request is moved after endpoint enumeration.
For all other controller drivers nothing changes, as there is no other
device communication between the lines where the set_configuration()
call moved.

Original-Change-Id: I6127627b9367ef573aa1a1525782bc1304ea350d
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com>
Reviewed-on: http://review.coreboot.org/3447
Tested-by: build bot (Jenkins)
Reviewed-by: Anton Kochkov <anton.kochkov@gmail.com>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
(cherry picked from commit 482af6d15ca8eed28b51edc11692815419326d65)

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

Change-Id: Ieb3af316a8d9aadb55a204b9f86281a511d14abd
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/168093
diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c
index 3ef372e..9789559 100644
--- a/payloads/libpayload/drivers/usb/ehci.c
+++ b/payloads/libpayload/drivers/usb/ehci.c
@@ -805,6 +805,9 @@
 	controller->shutdown = ehci_shutdown;
 	controller->bulk = ehci_bulk;
 	controller->control = ehci_control;
+	controller->set_address = generic_set_address;
+	controller->finish_device_config = NULL;
+	controller->destroy_device = NULL;
 	controller->create_intr_queue = ehci_create_intr_queue;
 	controller->destroy_intr_queue = ehci_destroy_intr_queue;
 	controller->poll_intr_queue = ehci_poll_intr_queue;
diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c
index 9c8e16e..9e4acfd 100644
--- a/payloads/libpayload/drivers/usb/ohci.c
+++ b/payloads/libpayload/drivers/usb/ohci.c
@@ -189,6 +189,9 @@
 	controller->shutdown = ohci_shutdown;
 	controller->bulk = ohci_bulk;
 	controller->control = ohci_control;
+	controller->set_address = generic_set_address;
+	controller->finish_device_config = NULL;
+	controller->destroy_device = NULL;
 	controller->create_intr_queue = ohci_create_intr_queue;
 	controller->destroy_intr_queue = ohci_destroy_intr_queue;
 	controller->poll_intr_queue = ohci_poll_intr_queue;
diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c
index 94d8552..3eff612 100644
--- a/payloads/libpayload/drivers/usb/uhci.c
+++ b/payloads/libpayload/drivers/usb/uhci.c
@@ -170,6 +170,9 @@
 	controller->shutdown = uhci_shutdown;
 	controller->bulk = uhci_bulk;
 	controller->control = uhci_control;
+	controller->set_address = generic_set_address;
+	controller->finish_device_config = NULL;
+	controller->destroy_device = NULL;
 	controller->create_intr_queue = uhci_create_intr_queue;
 	controller->destroy_intr_queue = uhci_destroy_intr_queue;
 	controller->poll_intr_queue = uhci_poll_intr_queue;
diff --git a/payloads/libpayload/drivers/usb/usb.c b/payloads/libpayload/drivers/usb/usb.c
index d28c8f8..a7ed4c2 100644
--- a/payloads/libpayload/drivers/usb/usb.c
+++ b/payloads/libpayload/drivers/usb/usb.c
@@ -242,13 +242,11 @@
 	return -1;		// no free address
 }
 
-static int
-set_address (hci_t *controller, int speed, int hubport, int hubaddr)
+int
+generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr)
 {
 	int adr = get_free_address (controller);	// address to set
 	dev_req_t dr;
-	configuration_descriptor_t *cd;
-	device_descriptor_t *dd;
 
 	memset (&dr, 0, sizeof (dr));
 	dr.data_dir = host_to_device;
@@ -277,7 +275,26 @@
 		return -1;
 	}
 	mdelay (50);
+
+	return adr;
+}
+
+static int
+set_address (hci_t *controller, int speed, int hubport, int hubaddr)
+{
+	int adr = controller->set_address(controller, speed, hubport, hubaddr);
+	if (adr < 0 || !controller->devices[adr]) {
+		usb_debug ("set_address failed\n");
+		return -1;
+	}
+	configuration_descriptor_t *cd;
+	device_descriptor_t *dd;
+
+	usbdev_t *dev = controller->devices[adr];
 	dev->address = adr;
+	dev->hub = hubaddr;
+	dev->port = hubport;
+	dev->speed = speed;
 	dev->descriptor = get_descriptor (dev, gen_bmRequestType
 		(device_to_host, standard_type, dev_recp), 1, 0, 0);
 	dd = (device_descriptor_t *) dev->descriptor;
@@ -298,7 +315,6 @@
 	dev->configuration = get_descriptor (dev, gen_bmRequestType
 		(device_to_host, standard_type, dev_recp), 2, 0, 0);
 	cd = (configuration_descriptor_t *) dev->configuration;
-	set_configuration (dev);
 	interface_descriptor_t *interface =
 		(interface_descriptor_t *) (((char *) cd) + cd->bLength);
 	{
@@ -366,6 +382,13 @@
 		}
 	}
 
+	if (controller->finish_device_config &&
+			controller->finish_device_config(dev))
+		return adr; /* Device isn't configured correctly,
+			       only control transfers may work. */
+
+	set_configuration(dev);
+
 	int class = dd->bDeviceClass;
 	if (class == 0)
 		class = interface->bInterfaceClass;
@@ -475,6 +498,8 @@
 		controller->devices[devno]->destroy (controller->devices[devno]);
 		free(controller->devices[devno]);
 		controller->devices[devno] = NULL;
+		if (controller->destroy_device)
+			controller->destroy_device(controller, devno);
 	}
 }
 
diff --git a/payloads/libpayload/include/usb/usb.h b/payloads/libpayload/include/usb/usb.h
index 33a2837..0296577 100644
--- a/payloads/libpayload/include/usb/usb.h
+++ b/payloads/libpayload/include/usb/usb.h
@@ -115,7 +115,7 @@
 typedef enum { OHCI = 0, UHCI = 1, EHCI = 2, XHCI = 3} hc_type;
 
 struct usbdev_hc {
-	struct usbdev_hc *next;
+	hci_t *next;
 	u32 reg_base;
 	hc_type type;
 	usbdev_t *devices[128];	// dev 0 is root hub, 127 is last addressable
@@ -142,6 +142,20 @@
 	void (*destroy_intr_queue) (endpoint_t *ep, void *queue);
 	u8* (*poll_intr_queue) (void *queue);
 	void *instance;
+
+	/* set_address():		Tell the usb device its address and
+					return it. xHCI controllers want to
+					do this by themself. Also, the usbdev
+					structure has to be allocated and
+					initialized. */
+	int (*set_address) (hci_t *controller, int speed, int hubport, int hubaddr);
+	/* finish_device_config():	Another hook for xHCI,
+					returns 0 on success. */
+	int (*finish_device_config) (usbdev_t *dev);
+	/* destroy_device():		Finally, destroy all structures that
+					were allocated during set_address()
+					and finish_device_config(). */
+	void (*destroy_device) (hci_t *controller, int devaddr);
 };
 
 typedef struct {
@@ -249,6 +263,9 @@
 	return (dir << 7) | (type << 5) | recp;
 }
 
+/* default "set address" handler */
+int generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr);
+
 void usb_detach_device(hci_t *controller, int devno);
 int usb_attach_device(hci_t *controller, int hubaddress, int port, int speed);