Patchwork USB Host Controller Drivers for libpayload

login
register
about
Submitter Patrick Georgi
Date 2010-08-12 14:35:29
Message ID <4C6406B1.7090308@georgi-clan.de>
Download mbox | patch
Permalink /patch/1734/
State Accepted
Headers show

Comments

Patrick Georgi - 2010-08-12 14:35:29
Hi,

attached patches add support for OHCI (USB1.1 in the non-intel/via
flavor) and xHCI (USB3) to the libpayload USB stack.

The code is the result of my Google Summer of Code project of this year.
A big "Thank You!" to Google for sponsoring this effort, and the Summer
of Code coordinators at Google for managing it.

0.improve-bus-scan:
- Properly report the bus number to the user
- Look if a device exists before iterating all its functions


1.ohci-driver:
Interrupt transfers work fine with UHCI, but it seems not to be a good
match for any other of the USB standards, so I'm looking at how to
improve it.
For this reason, the OHCI driver doesn't support interrupt transfers.

This limitation affects keyboard operation. Mass storage (thumb drives,
etc) are tested.


2.xhci-driver:
xHCI is detected and does "basic" initialization (meaning: it does more
complex things than the entire UHCI driver)
Unfortunately my xHCI hardware, the xHCI specification and/or my
understanding of either can't quite agree, so the driver is still
missing a couple of things and thus is not working yet.
The only user-visible capability (beyond printing lots of weird stuff on
the debug channel) is reliable attach/detach detection.


The patches must be applied in order due to their overlapping changes to
common files.

I'll continue to work on both drivers, but decided to post the code in
the current form already, to prevent last-minute pressure before the
Google Summer of Code deadline.


Signed-off-by: Patrick Georgi <patrick@georgi-clan.de>

Index: libpayload-release/Config.in
===================================================================
--- libpayload-release.orig/Config.in	2010-08-12 15:51:22.362598000 +0200
+++ libpayload-release/Config.in	2010-08-12 15:52:25.977110300 +0200
@@ -224,7 +224,6 @@
 	help
 	  Select this option if you are going to use USB 1.1 on an AMD based
 	  system.
-	  NOTE: This option is not (fully) implemented yet
 
 config USB_EHCI
 	bool "Support for USB EHCI controllers"
Index: libpayload-release/drivers/Makefile.inc
===================================================================
--- libpayload-release.orig/drivers/Makefile.inc	2010-08-12 15:51:22.362598000 +0200
+++ libpayload-release/drivers/Makefile.inc	2010-08-12 15:52:25.977110300 +0200
@@ -60,6 +60,8 @@
 TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o
 TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o
 TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o
+TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci.o
+TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci_rh.o
 TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o
 TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o
 
Index: libpayload-release/drivers/usb/ohci.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/ohci.c	2010-08-12 15:52:51.955155700 +0200
@@ -0,0 +1,472 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define USB_DEBUG
+
+#include <arch/virtual.h>
+#include <usb/usb.h>
+#include "ohci_private.h"
+#include "ohci.h"
+
+static void ohci_start (hci_t *controller);
+static void ohci_stop (hci_t *controller);
+static void ohci_reset (hci_t *controller);
+static void ohci_shutdown (hci_t *controller);
+static int ohci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
+static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
+			 int dalen, u8 *data);
+static void* ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
+static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue);
+static u8* ohci_poll_intr_queue (void *queue);
+
+static void
+ohci_reset (hci_t *controller)
+{
+}
+
+#ifdef USB_DEBUG
+/* Section 4.3.3 */
+static const char *completion_codes[] = {
+	"No error",
+	"CRC",
+	"Bit stuffing",
+	"Data toggle mismatch",
+	"Stall",
+	"Device not responding",
+	"PID check failure",
+	"Unexpected PID",
+	"Data overrun",
+	"Data underrun",
+	"--- (10)",
+	"--- (11)",
+	"Buffer overrun",
+	"Buffer underrun",
+	"Not accessed (14)",
+	"Not accessed (15)"
+};
+
+/* Section 4.3.1.2 */
+static const char *direction[] = {
+	"SETUP",
+	"OUT",
+	"IN",
+	"reserved / from TD"
+};
+#endif
+
+hci_t *
+ohci_init (pcidev_t addr)
+{
+	int i;
+
+	hci_t *controller = new_controller ();
+
+	if (!controller)
+		usb_fatal("Could not create USB controller instance.\n");
+
+	controller->instance = malloc (sizeof (ohci_t));
+	if(!controller->instance)
+		usb_fatal("Not enough memory creating USB controller instance.\n");
+
+	controller->start = ohci_start;
+	controller->stop = ohci_stop;
+	controller->reset = ohci_reset;
+	controller->shutdown = ohci_shutdown;
+	controller->bulk = ohci_bulk;
+	controller->control = ohci_control;
+	controller->create_intr_queue = ohci_create_intr_queue;
+	controller->destroy_intr_queue = ohci_destroy_intr_queue;
+	controller->poll_intr_queue = ohci_poll_intr_queue;
+	for (i = 0; i < 128; i++) {
+		controller->devices[i] = 0;
+	}
+	init_device_entry (controller, 0);
+	OHCI_INST (controller)->roothub = controller->devices[0];
+
+	controller->bus_address = addr;
+	controller->reg_base = pci_read_config32 (controller->bus_address, 0x10); // OHCI mandates MMIO, so bit 0 is clear
+	OHCI_INST (controller)->opreg = (opreg_t*)phys_to_virt(controller->reg_base);
+	printf("OHCI Version %x.%x\n", (OHCI_INST (controller)->opreg->HcRevision >> 4) & 0xf, OHCI_INST (controller)->opreg->HcRevision & 0xf);
+
+	if ((OHCI_INST (controller)->opreg->HcControl & HostControllerFunctionalStateMask) == USBReset) {
+		/* cold boot */
+		OHCI_INST (controller)->opreg->HcControl &= ~RemoteWakeupConnected;
+		OHCI_INST (controller)->opreg->HcFmInterval = (11999 * FrameInterval) | ((((11999 - 210)*6)/7) * FSLargestDataPacket);
+		/* TODO: right value for PowerOnToPowerGoodTime ? */
+		OHCI_INST (controller)->opreg->HcRhDescriptorA = NoPowerSwitching | NoOverCurrentProtection | (10 * PowerOnToPowerGoodTime);
+		OHCI_INST (controller)->opreg->HcRhDescriptorB = (0 * DeviceRemovable);
+		udelay(100); /* TODO: reset asserting according to USB spec */
+	} else if ((OHCI_INST (controller)->opreg->HcControl & HostControllerFunctionalStateMask) != USBOperational) {
+		OHCI_INST (controller)->opreg->HcControl = (OHCI_INST (controller)->opreg->HcControl & ~HostControllerFunctionalStateMask) | USBResume;
+		udelay(100); /* TODO: resume time according to USB spec */
+	}
+	int interval = OHCI_INST (controller)->opreg->HcFmInterval;
+
+	td_t *periodic_td = memalign(sizeof(*periodic_td), sizeof(*periodic_td));
+	memset((void*)periodic_td, 0, sizeof(*periodic_td));
+	for (i=0; i<32; i++) OHCI_INST (controller)->hcca->HccaInterruptTable[i] = virt_to_phys(periodic_td);
+	/* TODO: build HCCA data structures */
+
+	OHCI_INST (controller)->opreg->HcCommandStatus = HostControllerReset;
+	udelay (10); /* at most 10us for reset to complete. State must be set to Operational within 2ms (5.1.1.4) */
+	OHCI_INST (controller)->opreg->HcFmInterval = interval;
+	OHCI_INST (controller)->hcca = memalign(256, 256);
+	memset((void*)OHCI_INST (controller)->hcca, 0, 256);
+
+	OHCI_INST (controller)->opreg->HcHCCA = virt_to_phys(OHCI_INST (controller)->hcca);
+	OHCI_INST (controller)->opreg->HcControl &= ~IsochronousEnable; // unused by this driver
+	OHCI_INST (controller)->opreg->HcControl |= BulkListEnable; // always enabled. OHCI still sleeps on BulkListFilled
+	OHCI_INST (controller)->opreg->HcControl |= ControlListEnable; // dito
+	OHCI_INST (controller)->opreg->HcControl |= PeriodicListEnable; // FIXME: setup interrupt data structures and enable all the time
+	// disable everything, contrary to what OHCI spec says in 5.1.1.4, as we don't need IRQs
+	OHCI_INST (controller)->opreg->HcInterruptEnable = 1<<31;
+	OHCI_INST (controller)->opreg->HcInterruptDisable = ~(1<<31);
+	OHCI_INST (controller)->opreg->HcInterruptStatus = ~0;
+	OHCI_INST (controller)->opreg->HcPeriodicStart = (((OHCI_INST (controller)->opreg->HcFmInterval & FrameIntervalMask) / 10) * 9);
+	OHCI_INST (controller)->opreg->HcControl = (OHCI_INST (controller)->opreg->HcControl & ~HostControllerFunctionalStateMask) | USBOperational;
+
+	mdelay(100);
+
+	controller->devices[0]->controller = controller;
+	controller->devices[0]->init = ohci_rh_init;
+	controller->devices[0]->init (controller->devices[0]);
+	ohci_reset (controller);
+	return controller;
+}
+
+static void
+ohci_shutdown (hci_t *controller)
+{
+	if (controller == 0)
+		return;
+	detach_controller (controller);
+	ohci_stop(controller);
+	OHCI_INST (controller)->roothub->destroy (OHCI_INST (controller)->
+						  roothub);
+	free (OHCI_INST (controller));
+	free (controller);
+}
+
+static void
+ohci_start (hci_t *controller)
+{
+// TODO: turn on all operation of OHCI, but assume that it's initialized.
+}
+
+static void
+ohci_stop (hci_t *controller)
+{
+// TODO: turn off all operation of OHCI
+}
+
+static void
+dump_td(td_t *cur, int level)
+{
+#ifdef USB_DEBUG
+	static const char *spaces="          ";
+	const char *spc=spaces+(10-level);
+#endif
+	debug("%std at %x (%s), condition code: %s\n", spc, cur, direction[cur->direction], completion_codes[cur->condition_code & 0xf]);
+	debug("%s toggle: %x\n", spc, cur->toggle);
+}
+
+static int
+wait_for_ed(usbdev_t *dev, ed_t *head)
+{
+	td_t *cur;
+
+	/* wait for results */
+	while (((head->head_pointer & ~3) != head->tail_pointer) &&
+		!(head->head_pointer & 1) &&
+		((((td_t*)phys_to_virt(head->head_pointer & ~3))->condition_code & 0xf)>=0xe)) {
+		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,
+			OHCI_INST(dev->controller)->opreg->HcCommandStatus,
+			head->head_pointer,
+			((td_t*)phys_to_virt(head->head_pointer & ~3))->next_td,
+			head->tail_pointer,
+			((td_t*)phys_to_virt(head->head_pointer & ~3))->condition_code);
+		mdelay(1);
+	}
+	if (OHCI_INST(dev->controller)->opreg->HcInterruptStatus & WritebackDoneHead) {
+		debug("done queue:\n");
+		debug("%x, %x\n", OHCI_INST(dev->controller)->hcca->HccaDoneHead, phys_to_virt(OHCI_INST(dev->controller)->hcca->HccaDoneHead));
+		if ((OHCI_INST(dev->controller)->hcca->HccaDoneHead & ~1) == 0) {
+			debug("HcInterruptStatus %x\n", OHCI_INST(dev->controller)->opreg->HcInterruptStatus);
+		}
+		td_t *done_queue = NULL;
+		td_t *done_head = (td_t*)phys_to_virt(OHCI_INST(dev->controller)->hcca->HccaDoneHead);
+		OHCI_INST(dev->controller)->opreg->HcInterruptStatus = WritebackDoneHead;
+		while (1) {
+			td_t *oldnext = (td_t*)phys_to_virt(done_head->next_td);
+			if (oldnext == done_queue) break; /* last element refers to second to last, ie. endless loop */
+			if (oldnext == phys_to_virt(0)) break; /* last element of done list == first element of real list */
+			debug("head is %x, pointing to %x. requeueing to %x\n", done_head, oldnext, done_queue);
+			done_head->next_td = (u32)done_queue;
+			done_queue = done_head;
+			done_head = oldnext;
+		}
+		for (cur = done_queue; cur != 0; cur = (td_t*)cur->next_td) {
+			dump_td(cur, 1);
+		}
+	}
+
+	if (head->head_pointer & 1) {
+		debug("HALTED!\n");
+		return 1;
+	}
+	return 0;
+}
+
+static int
+ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
+	      unsigned char *data)
+{
+	int i;
+
+	td_t *cur;
+
+	// pages are specified as 4K in OHCI, so don't use getpagesize()
+	int first_page = (unsigned long)data / 4096;
+	int last_page = (unsigned long)(data+dalen-1)/4096;
+	if (last_page < first_page) last_page = first_page;
+	int pages = (dalen==0)?0:(last_page - first_page + 1);
+	int td_count = (pages+1)/2;
+
+	td_t *tds = memalign(sizeof(td_t), (td_count+3)*sizeof(td_t));
+	memset((void*)tds, 0, (td_count+3)*sizeof(td_t));
+
+	for (i=0; i < td_count + 3; i++) {
+		tds[i].next_td = virt_to_phys(&tds[i+1]);
+	}
+	tds[td_count + 3].next_td = 0;
+
+	tds[0].direction = OHCI_SETUP;
+	tds[0].toggle_from_td = 1;
+	tds[0].toggle = 0;
+	tds[0].error_count = 0;
+	tds[0].delay_interrupt = 7;
+	tds[0].condition_code = 0xf;
+	tds[0].current_buffer_pointer = virt_to_phys(devreq);
+	tds[0].buffer_end = virt_to_phys(devreq + drlen - 1);
+
+	cur = &tds[0];
+
+	while (pages > 0) {
+		cur++;
+		cur->direction = (dir==IN)?OHCI_IN:OHCI_OUT;
+		cur->toggle_from_td = 0;
+		cur->toggle = 1;
+		cur->error_count = 0;
+		cur->delay_interrupt = 7;
+		cur->condition_code = 0xf;
+		cur->current_buffer_pointer = virt_to_phys(data);
+		pages--;
+		int consumed = (4096 - ((unsigned long)data % 4096));
+		if (consumed >= dalen) {
+			// end of data is within same page
+			cur->buffer_end = virt_to_phys(data + dalen - 1);
+			dalen = 0;
+			/* assert(pages == 0); */
+		} else {
+			dalen -= consumed;
+			data += consumed;
+			pages--;
+			int second_page_size = dalen;
+			if (dalen > 4096) {
+				second_page_size = 4096;
+			}
+			cur->buffer_end = virt_to_phys(data + second_page_size - 1);
+			dalen -= second_page_size;
+			data += second_page_size;
+		}
+	}
+
+	cur++;
+	cur->direction = (dir==IN)?OHCI_OUT:OHCI_IN;
+	cur->toggle_from_td = 1;
+	cur->toggle = 1;
+	cur->error_count = 0;
+	cur->delay_interrupt = 7;
+	cur->condition_code = 0xf;
+	cur->current_buffer_pointer = 0;
+	cur->buffer_end = 0;
+
+	/* final dummy TD */
+	cur++;
+
+	/* Data structures */
+	ed_t *head = memalign(sizeof(ed_t), sizeof(ed_t));
+	memset((void*)head, 0, sizeof(*head));
+	head->function_address = dev->address;
+	head->endpoint_number = 0;
+	head->direction = OHCI_FROM_TD;
+	head->lowspeed = dev->speed;
+	head->format = 0;
+	head->maximum_packet_size = dev->endpoints[0].maxpacketsize;
+	head->tail_pointer = virt_to_phys(cur);
+	head->head_pointer = virt_to_phys(tds);
+	head->halted = 0;
+	head->toggle = 0;
+
+	debug("doing control transfer with %x. first_td at %x\n", head->function_address, virt_to_phys(tds));
+
+	/* activate schedule */
+	OHCI_INST(dev->controller)->opreg->HcControlHeadED = virt_to_phys(head);
+	OHCI_INST(dev->controller)->opreg->HcCommandStatus = ControlListFilled;
+
+	int failure = wait_for_ed(dev, head);
+
+	/* free memory */
+	free((void*)tds);
+	free((void*)head);
+
+	return failure;
+}
+
+/* finalize == 1: if data is of packet aligned size, add a zero length packet */
+static int
+ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
+{
+	int i;
+	debug("bulk: %x bytes from %x, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize);
+
+	td_t *cur;
+
+	// pages are specified as 4K in OHCI, so don't use getpagesize()
+	int first_page = (unsigned long)data / 4096;
+	int last_page = (unsigned long)(data+dalen-1)/4096;
+	if (last_page < first_page) last_page = first_page;
+	int pages = (dalen==0)?0:(last_page - first_page + 1);
+	int td_count = (pages+1)/2;
+
+	if (finalize && ((dalen % ep->maxpacketsize) == 0)) {
+		td_count++;
+	}
+
+	td_t *tds = memalign(sizeof(td_t), (td_count+1)*sizeof(td_t));
+	memset((void*)tds, 0, (td_count+1)*sizeof(td_t));
+
+	for (i=0; i < td_count; i++) {
+		tds[i].next_td = virt_to_phys(&tds[i+1]);
+	}
+
+	for (cur = tds; cur->next_td != 0; cur++) {
+		cur->toggle_from_td = 0;
+		cur->error_count = 0;
+		cur->delay_interrupt = 7;
+		cur->condition_code = 0xf;
+		cur->direction = (ep->direction==IN)?OHCI_IN:OHCI_OUT;
+		pages--;
+		if (dalen == 0) {
+			/* magic TD for empty packet transfer */
+			cur->current_buffer_pointer = 0;
+			cur->buffer_end = 0;
+			/* assert((pages == 0) && finalize); */
+		}
+		int consumed = (4096 - ((unsigned long)data % 4096));
+		if (consumed >= dalen) {
+			// end of data is within same page
+			cur->buffer_end = virt_to_phys(data + dalen - 1);
+			dalen = 0;
+			/* assert(pages == finalize); */
+		} else {
+			dalen -= consumed;
+			data += consumed;
+			pages--;
+			int second_page_size = dalen;
+			if (dalen > 4096) {
+				second_page_size = 4096;
+			}
+			cur->buffer_end = virt_to_phys(data + second_page_size - 1);
+			dalen -= second_page_size;
+			data += second_page_size;
+		}
+	}
+
+	/* Data structures */
+	ed_t *head = memalign(sizeof(ed_t), sizeof(ed_t));
+	memset((void*)head, 0, sizeof(*head));
+	head->function_address = ep->dev->address;
+	head->endpoint_number = ep->endpoint & 0xf;
+	head->direction = (ep->direction==IN)?OHCI_IN:OHCI_OUT;
+	head->lowspeed = ep->dev->speed;
+	head->format = 0;
+	head->maximum_packet_size = ep->maxpacketsize;
+	head->tail_pointer = virt_to_phys(cur);
+	head->head_pointer = virt_to_phys(tds);
+	head->halted = 0;
+	head->toggle = ep->toggle;
+
+	debug("doing bulk transfer with %x(%x). first_td at %x, last %x\n", head->function_address, head->endpoint_number, virt_to_phys(tds), virt_to_phys(cur));
+
+	/* activate schedule */
+	OHCI_INST(ep->dev->controller)->opreg->HcBulkHeadED = virt_to_phys(head);
+	OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = BulkListFilled;
+
+	int failure = wait_for_ed(ep->dev, head);
+
+	ep->toggle = head->toggle;
+
+	/* free memory */
+	free((void*)tds);
+	free((void*)head);
+
+	if (failure) {
+		/* try cleanup */
+		clear_stall(ep);
+	}
+
+	return failure;
+}
+
+/* create and hook-up an intr queue into device schedule */
+static void*
+ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
+{
+	return NULL;
+}
+
+/* remove queue from device schedule, dropping all data that came in */
+static void
+ohci_destroy_intr_queue (endpoint_t *ep, void *q_)
+{
+}
+
+/* read one intr-packet from queue, if available. extend the queue for new input.
+   return NULL if nothing new available.
+   Recommended use: while (data=poll_intr_queue(q)) process(data);
+ */
+static u8*
+ohci_poll_intr_queue (void *q_)
+{
+	return NULL;
+}
+
Index: libpayload-release/drivers/usb/ohci.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/ohci.h	2010-08-12 15:52:15.603092200 +0200
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __OHCI_H
+#define __OHCI_H
+
+#include <pci.h>
+#include <usb/usb.h>
+
+     hci_t *ohci_init (pcidev_t addr);
+
+     void ohci_rh_init (usbdev_t *dev);
+
+#endif
Index: libpayload-release/drivers/usb/ohci_private.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/ohci_private.h	2010-08-12 15:52:15.603092200 +0200
@@ -0,0 +1,255 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __OHCI_PRIVATE_H
+#define __OHCI_PRIVATE_H
+
+#include <pci.h>
+#include <usb/usb.h>
+
+#define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit))
+
+	// FIXME: fake
+	typedef enum { CMD} reg;
+
+	enum {
+		NumberDownstreamPorts = 1<<0,
+		PowerSwitchingMode = 1<<8,
+		NoPowerSwitching = 1<<9,
+		DeviceType = 1<<10,
+		OverCurrentProtectionMode = 1<<11,
+		NoOverCurrentProtection = 1<<12,
+		PowerOnToPowerGoodTime = 1<<24
+	} HcRhDescriptorAReg;
+
+	enum {
+		NumberDownstreamPortsMask = MASK(0, 8),
+		PowerOnToPowerGoodTimeMask = MASK(24, 8)
+	} HcRhDescriptorAMask;
+
+	enum {
+		DeviceRemovable = 1<<0,
+		PortPowerControlMask = 1<<16
+	} HcRhDescriptorBReg;
+
+	enum {
+		CurrentConnectStatus		= 1<<0,
+		PortEnableStatus		= 1<<1,
+		PortSuspendStatus		= 1<<2,
+		PortOverCurrentIndicator	= 1<<3,
+		PortResetStatus			= 1<<4,
+		PortPowerStatus			= 1<<8,
+		LowSpeedDeviceAttached		= 1<<9,
+		ConnectStatusChange		= 1<<16,
+		PortEnableStatusChange		= 1<<17,
+		PortSuspendStatusChange		= 1<<18,
+		PortOverCurrentIndicatorChange	= 1<<19,
+		PortResetStatusChange		= 1<<20
+	} HcRhPortStatusRead;
+	enum {
+		ClearPortEnable			= 1<<0,
+		SetPortEnable			= 1<<1,
+		SetPortSuspend			= 1<<2,
+		ClearSuspendStatus		= 1<<3,
+		SetPortReset			= 1<<4,
+		SetPortPower			= 1<<8,
+		ClearPortPower			= 1<<9,
+	} HcRhPortStatusSet;
+
+	enum {
+		LocalPowerStatus = 1<<0,
+		OverCurrentIndicator = 1<<1,
+		DeviceRemoteWakeupEnable = 1<<15,
+		LocalPowerStatusChange = 1<<16,
+		OverCurrentIndicatorChange = 1<<17,
+		ClearRemoteWakeupEnable = 1<<31
+	} HcRhStatusReg;
+
+	enum {
+		FrameInterval = 1<<0,
+		FSLargestDataPacket = 1<<16,
+		FrameIntervalToggle = 1<<31
+	} HcFmIntervalOffset;
+	enum {
+		FrameIntervalMask = MASK(0, 14),
+		FSLargestDataPacketMask = MASK(16, 15),
+		FrameIntervalToggleMask = MASK(31, 1)
+	} HcFmIntervalMask;
+
+	enum {
+		ControlBulkServiceRatio = 1<<0,
+		PeriodicListEnable = 1<<2,
+		IsochronousEnable = 1<<3,
+		ControlListEnable = 1<<4,
+		BulkListEnable = 1<<5,
+		HostControllerFunctionalState = 1<<6,
+		InterruptRouting = 1<<8,
+		RemoteWakeupConnected = 1<<9,
+		RemoteWakeupEnable = 1<<10
+	} HcControlReg;
+
+	enum {
+		ControlBulkServiceRatioMask = MASK(0, 2),
+		HostControllerFunctionalStateMask = MASK(6, 2)
+	} HcControlMask;
+
+	enum {
+		USBReset = 0*HostControllerFunctionalState,
+		USBResume = 1*HostControllerFunctionalState,
+		USBOperational = 2*HostControllerFunctionalState,
+		USBSuspend = 3*HostControllerFunctionalState
+	};
+
+	enum {
+		HostControllerReset = 1<<0,
+		ControlListFilled = 1<<1,
+		BulkListFilled = 1<<2,
+		OwnershipChangeRequest = 1<<3,
+		SchedulingOverrunCount = 1<<16
+	} HcCommandStatusReg;
+
+	enum {
+		SchedulingOverrunCountMask = MASK(16, 2)
+	} HcCommandStatusMask;
+
+	enum {
+		FrameRemaining = 1<<0,
+		FrameRemainingToggle = 1<<31
+	} HcFmRemainingReg;
+
+	enum {
+		SchedulingOverrung = 1<<0,
+		WritebackDoneHead = 1<<1,
+		StartofFrame = 1<<2,
+		ResumeDetected = 1<<3,
+		UnrecoverableError = 1<<4,
+		FrameNumberOverflow = 1<<5,
+		RootHubStatusChange = 1<<6,
+		OwnershipChange = 1<<30
+	} HcInterruptStatusReg;
+
+     typedef struct {
+	// Control and Status Partition
+	volatile u32 HcRevision;
+	volatile u32 HcControl;
+	volatile u32 HcCommandStatus;
+	volatile u32 HcInterruptStatus;
+	volatile u32 HcInterruptEnable;
+	volatile u32 HcInterruptDisable;
+
+	// Memory Pointer Partition
+	volatile u32 HcHCCA;
+	volatile u32 HcPeriodCurrentED;
+	volatile u32 HcControlHeadED;
+	volatile u32 HcControlCurrentED;
+	volatile u32 HcBulkHeadED;
+	volatile u32 HcBulkCurrentED;
+	volatile u32 HcDoneHead;
+
+	// Frame Counter Partition
+	volatile u32 HcFmInterval;
+	volatile u32 HcFmRemaining;
+	volatile u32 HcFmNumber;
+	volatile u32 HcPeriodicStart;
+	volatile u32 HcLSThreshold;
+
+	// Root Hub Partition
+	volatile u32 HcRhDescriptorA;
+	volatile u32 HcRhDescriptorB;
+	volatile u32 HcRhStatus;
+	/* all bits in HcRhPortStatus registers are R/WC, so
+	   _DO NOT_ use |= to set the bits,
+	   this clears the entire state */
+	volatile u32 HcRhPortStatus[];
+     } __attribute__ ((packed)) opreg_t;
+
+ 	typedef struct {
+		u32 HccaInterruptTable[32];
+		u16 HccaFrameNumber;
+		u16 HccaPad1;
+		u32 HccaDoneHead;
+		u8 reserved[116]; // pad to 256 byte
+	} __attribute__ ((packed)) hcca_t;
+
+	typedef struct ohci {
+		opreg_t *opreg;
+		hcca_t *hcca;
+		usbdev_t *roothub;
+	} ohci_t;
+
+	typedef enum { OHCI_SETUP=0, OHCI_OUT=1, OHCI_IN=2, OHCI_FROM_TD=3 } ohci_pid_t;
+
+	typedef volatile struct {
+		union {
+			u32 dword0;
+			struct {
+				unsigned long function_address:7;
+				unsigned long endpoint_number:4;
+				unsigned long direction:2;
+				unsigned long lowspeed:1;
+				unsigned long skip:1;
+				unsigned long format:1;
+				unsigned long maximum_packet_size:11;
+				unsigned long:5;
+			} __attribute__ ((packed));
+		};
+		u32 tail_pointer;
+		union {
+			u32 head_pointer;
+			struct {
+				unsigned long halted:1;
+				unsigned long toggle:1;
+				unsigned long:30;
+			} __attribute__ ((packed));
+		};
+		u32 next_ed;
+	} __attribute__ ((packed)) ed_t;
+
+	typedef volatile struct {
+		union {
+			u32 dword0;
+			struct {
+				unsigned long:18;
+				unsigned long buffer_rounding:1;
+				unsigned long direction:2;
+				unsigned long delay_interrupt:3;
+				unsigned long toggle:1;
+				unsigned long toggle_from_td:1;
+				unsigned long error_count:2;
+				unsigned long condition_code:4;
+			} __attribute__ ((packed));
+		};
+		u32 current_buffer_pointer;
+		u32 next_td;
+		u32 buffer_end;
+	} __attribute__ ((packed)) td_t;
+
+#define OHCI_INST(controller) ((ohci_t*)((controller)->instance))
+
+#endif
Index: libpayload-release/drivers/usb/ohci_rh.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/ohci_rh.c	2010-08-12 15:52:15.618692200 +0200
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+//#define USB_DEBUG
+
+#include <libpayload.h>
+#include "ohci_private.h"
+#include "ohci.h"
+
+typedef struct {
+	int numports;
+	int *port;
+} rh_inst_t;
+
+#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
+
+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;
+}
+
+/* disable root hub */
+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);
+}
+
+static void
+ohci_rh_scanport (usbdev_t *dev, int port)
+{
+	if (port >= RH_INST(dev)->numports) {
+		debug("Invalid port %d\n", port);
+		return;
+	}
+
+	/* device registered, and device change logged, so something must have happened */
+	if (RH_INST (dev)->port[port] != -1) {
+		usb_detach_device(dev->controller, RH_INST (dev)->port[port]);
+		RH_INST (dev)->port[port] = -1;
+	}
+
+	/* no device attached
+	   previously registered devices are detached, nothing left to do */
+	if (!(OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & CurrentConnectStatus))
+		return;
+
+	OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = ConnectStatusChange; // clear port state change
+	ohci_rh_enable_port (dev, port);
+
+	mdelay(100); // wait for signal to stabilize
+
+	if (!(OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & PortEnableStatus)) {
+		debug ("port enable failed\n");
+		return;
+	}
+
+	int speed = (OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] & LowSpeedDeviceAttached) != 0;
+	RH_INST (dev)->port[port] = usb_attach_device(dev->controller, dev->address, port, speed);
+}
+
+static int
+ohci_rh_report_port_changes (usbdev_t *dev)
+{
+	int i;
+
+	if (!(OHCI_INST (dev->controller)->opreg->HcInterruptStatus & RootHubStatusChange)) return -1;
+	OHCI_INST (dev->controller)->opreg->HcInterruptStatus = RootHubStatusChange;
+	debug("port change\n");
+
+	for (i = 0; i < RH_INST(dev)->numports; i++) {
+		// maybe detach+attach happened between two scans?
+		if (OHCI_INST (dev->controller)->opreg->HcRhPortStatus[i] & ConnectStatusChange) {
+			debug("attachment change on port %d\n", i);
+			return i;
+		}
+	}
+
+	// no change
+	return -1;
+}
+
+static void
+ohci_rh_destroy (usbdev_t *dev)
+{
+	int i;
+	for (i = 0; i < RH_INST (dev)->numports; i++)
+		ohci_rh_disable_port (dev, i);
+	free (RH_INST (dev));
+}
+
+static void
+ohci_rh_poll (usbdev_t *dev)
+{
+	int port;
+	while ((port = ohci_rh_report_port_changes (dev)) != -1)
+		ohci_rh_scanport (dev, port);
+}
+
+void
+ohci_rh_init (usbdev_t *dev)
+{
+	int i;
+
+	dev->destroy = ohci_rh_destroy;
+	dev->poll = ohci_rh_poll;
+
+	dev->data = malloc (sizeof (rh_inst_t));
+	if (!dev->data)
+		usb_fatal ("Not enough memory for OHCI RH.\n");
+
+	RH_INST (dev)->numports = OHCI_INST (dev->controller)->opreg->HcRhDescriptorA & NumberDownstreamPortsMask;
+	RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports);
+	debug("%d ports registered\n", RH_INST (dev)->numports);
+
+	for (i = 0; i < RH_INST (dev)->numports; i++) {
+		ohci_rh_enable_port (dev, i);
+		RH_INST (dev)->port[i] = -1;
+	}
+
+	/* we can set them here because a root hub _really_ shouldn't
+	   appear elsewhere */
+	dev->address = 0;
+	dev->hub = -1;
+	dev->port = -1;
+
+	debug("rh init done\n");
+}
Index: libpayload-release/drivers/usb/usbinit.c
===================================================================
--- libpayload-release.orig/drivers/usb/usbinit.c	2010-08-12 15:52:12.826287400 +0200
+++ libpayload-release/drivers/usb/usbinit.c	2010-08-12 15:52:25.977110300 +0200
@@ -30,7 +30,7 @@
 #include <libpayload-config.h>
 #include <usb/usb.h>
 #include "uhci.h"
-//#include "ohci.h"
+#include "ohci.h"
 //#include "ehci.h"
 //#include "xhci.h"
 #include <usb/usbdisk.h>
@@ -81,8 +81,7 @@
 		if (prog_if == 0x10) {
 			printf ("OHCI controller\n");
 #ifdef CONFIG_USB_OHCI
-			//ohci_init(addr);
-			printf ("Not supported.\n");
+			ohci_init(addr);
 #else
 			printf ("Not supported.\n");
 #endif
Index: libpayload-release/Config.in
===================================================================
--- libpayload-release.orig/Config.in	2010-08-12 15:52:25.977110300 +0200
+++ libpayload-release/Config.in	2010-08-12 15:53:07.363982700 +0200
@@ -232,6 +232,13 @@
 	  Select this option if you want to use USB 2.0
 	  NOTE: This option is not (fully) implemented yet
 
+config USB_XHCI
+	bool "Support for USB xHCI controllers"
+	depends on USB
+	help
+	  Select this option if you want to use USB 3.0
+	  NOTE: This option is not (fully) implemented yet
+
 config USB_HID
 	bool "Support for USB keyboards"
 	depends on USB
Index: libpayload-release/drivers/Makefile.inc
===================================================================
--- libpayload-release.orig/drivers/Makefile.inc	2010-08-12 15:52:25.977110300 +0200
+++ libpayload-release/drivers/Makefile.inc	2010-08-12 15:53:07.379582800 +0200
@@ -62,6 +62,8 @@
 TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o
 TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci.o
 TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci_rh.o
+TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci.o
+TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci_rh.o
 TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o
 TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o
 
Index: libpayload-release/drivers/usb/xhci.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/xhci.c	2010-08-12 15:53:44.242447000 +0200
@@ -0,0 +1,262 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define USB_DEBUG
+
+#include <arch/virtual.h>
+#include "xhci.h"
+#include "xhci_private.h"
+
+static void xhci_start (hci_t *controller);
+static void xhci_stop (hci_t *controller);
+static void xhci_reset (hci_t *controller);
+static void xhci_shutdown (hci_t *controller);
+static int xhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
+static int xhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
+			 int dalen, u8 *data);
+static void* xhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
+static void xhci_destroy_intr_queue (endpoint_t *ep, void *queue);
+static u8* xhci_poll_intr_queue (void *queue);
+
+static void
+xhci_reset (hci_t *controller)
+{
+}
+
+hci_t *
+xhci_init (pcidev_t addr)
+{
+	int i;
+
+	hci_t *controller = new_controller ();
+
+	if (!controller)
+		usb_fatal("Could not create USB controller instance.\n");
+
+	controller->instance = malloc (sizeof (xhci_t));
+	if(!controller->instance)
+		usb_fatal("Not enough memory creating USB controller instance.\n");
+
+	controller->start = xhci_start;
+	controller->stop = xhci_stop;
+	controller->reset = xhci_reset;
+	controller->shutdown = xhci_shutdown;
+	controller->bulk = xhci_bulk;
+	controller->control = xhci_control;
+	controller->create_intr_queue = xhci_create_intr_queue;
+	controller->destroy_intr_queue = xhci_destroy_intr_queue;
+	controller->poll_intr_queue = xhci_poll_intr_queue;
+	for (i = 0; i < 128; i++) {
+		controller->devices[i] = 0;
+	}
+	init_device_entry (controller, 0);
+	XHCI_INST (controller)->roothub = controller->devices[0];
+
+	controller->bus_address = addr;
+	controller->reg_base = (u32)phys_to_virt(pci_read_config32 (controller->bus_address, 0x10) & ~0xf);
+	//controller->reg_base = pci_read_config32 (controller->bus_address, 0x14) & ~0xf;
+	if (pci_read_config32 (controller->bus_address, 0x14) > 0) {
+		usb_fatal("We don't do 64bit addressing.\n");
+	}
+	debug("regbase: %lx\n", controller->reg_base);
+
+	XHCI_INST (controller)->capreg = (void*)controller->reg_base;
+	XHCI_INST (controller)->opreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->caplength);
+	XHCI_INST (controller)->hcrreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->rtsoff);
+	XHCI_INST (controller)->dbreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->dboff);
+	debug("caplen: %lx\nrtsoff: %lx\ndboff: %lx\n", XHCI_INST (controller)->capreg->caplength, XHCI_INST (controller)->capreg->rtsoff, XHCI_INST (controller)->capreg->dboff);
+	debug("caplength: %x\n", XHCI_INST (controller)->capreg->caplength);
+	debug("hciversion: %x.%x\n", XHCI_INST (controller)->capreg->hciver_hi, XHCI_INST (controller)->capreg->hciver_lo);
+	if ((XHCI_INST (controller)->capreg->hciversion < 0x96) || (XHCI_INST (controller)->capreg->hciversion > 0x100)) {
+		usb_fatal("Unsupported xHCI version\n");
+	}
+	debug("maxslots: %x\n", XHCI_INST (controller)->capreg->MaxSlots);
+	debug("maxports: %x\n", XHCI_INST (controller)->capreg->MaxPorts);
+	int pagesize = XHCI_INST (controller)->opreg->pagesize << 12;
+	debug("pagesize: %x\n", pagesize);
+
+	XHCI_INST (controller)->dcbaa = memalign(64, (XHCI_INST (controller)->capreg->MaxSlots+1)*sizeof(devctxp_t));
+	memset((void*)XHCI_INST (controller)->dcbaa, 0, (XHCI_INST (controller)->capreg->MaxSlots+1)*sizeof(devctxp_t));
+
+	debug("max scratchpad bufs: %x\n", XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs);
+	if (XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs > 0) {
+		XHCI_INST (controller)->dcbaa->ptr = memalign(64, XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs * 8);
+	}
+
+	XHCI_INST (controller)->opreg->dcbaap_lo = virt_to_phys(XHCI_INST (controller)->dcbaa);
+	XHCI_INST (controller)->opreg->dcbaap_hi = 0;
+
+	printf("waiting for controller to be ready - ");
+	while ((XHCI_INST (controller)->opreg->usbsts & USBSTS_CNR) != 0) mdelay(1);
+	printf("ok.\n");
+
+	debug("ERST Max: %lx -> %lx entries\n", XHCI_INST (controller)->capreg->ERST_Max, 1<<(XHCI_INST (controller)->capreg->ERST_Max));
+
+	// enable all available slots
+	XHCI_INST (controller)->opreg->config = XHCI_INST (controller)->capreg->MaxSlots & CONFIG_MASK_MaxSlotsEn;
+
+	XHCI_INST (controller)->cmd_ring = memalign(64, 16*sizeof(trb_t)); /* TODO: make sure not to cross 64k page boundary */
+	memset((void*)XHCI_INST (controller)->cmd_ring, 0, 16*sizeof(trb_t));
+
+	XHCI_INST (controller)->ev_ring = memalign(64, 16*sizeof(trb_t)); /* TODO: make sure not to cross 64k page boundary */
+	memset((void*)XHCI_INST (controller)->ev_ring, 0, 16*sizeof(trb_t));
+
+	XHCI_INST (controller)->ev_ring_table = memalign(64, sizeof(erst_entry_t));
+	memset((void*)XHCI_INST (controller)->ev_ring_table, 0, sizeof(erst_entry_t));
+	XHCI_INST (controller)->ev_ring_table[0].seg_base_lo = virt_to_phys(XHCI_INST (controller)->ev_ring);
+	XHCI_INST (controller)->ev_ring_table[0].seg_base_hi = 0;
+	XHCI_INST (controller)->ev_ring_table[0].seg_size = 16;
+
+	// init command ring
+	XHCI_INST (controller)->opreg->crcr_lo = virt_to_phys(XHCI_INST (controller)->cmd_ring) | CRCR_RCS;
+	XHCI_INST (controller)->opreg->crcr_hi = 0;
+	XHCI_INST (controller)->cmd_ccs = 1;
+	XHCI_INST (controller)->ev_ccs = 1;
+
+	// init primary interrupter
+	XHCI_INST (controller)->hcrreg->intrrs[0].erstsz = 1;
+	XHCI_INST (controller)->hcrreg->intrrs[0].erdp_lo = virt_to_phys(XHCI_INST (controller)->ev_ring);
+	XHCI_INST (controller)->hcrreg->intrrs[0].erdp_hi = 0;
+	XHCI_INST (controller)->hcrreg->intrrs[0].erstba_lo = virt_to_phys(XHCI_INST (controller)->ev_ring_table);
+	XHCI_INST (controller)->hcrreg->intrrs[0].erstba_hi = 0;
+
+	XHCI_INST (controller)->opreg->usbcmd |= USBCMD_RS; /* start USB controller */
+	XHCI_INST (controller)->dbreg[0] = 0; // and tell controller to consume commands
+
+	/* TODO: TEST */
+	// setup noop command
+	trb_t *cmd = &XHCI_INST (controller)->cmd_ring[0];
+	((u32*)cmd)[3] = 1-XHCI_INST (controller)->cmd_ccs; // disable command descriptor
+	((u32*)cmd)[0] = 0;
+	((u32*)cmd)[1] = 0;
+	((u32*)cmd)[2] = 0;
+	cmd->cmd_No_Op.TRB_Type = TRB_CMD_NOOP;
+
+	// ring the HC doorbell
+	debug("Posting command at %lx\n", virt_to_phys(cmd));
+	cmd->cmd_No_Op.C = XHCI_INST (controller)->cmd_ccs; // enable command
+	XHCI_INST (controller)->dbreg[0] = 0; // and tell controller to consume commands
+
+	// wait for result in event ring
+	trb_t *ev = &XHCI_INST (controller)->ev_ring[0];
+	trb_t *ev1 = &XHCI_INST (controller)->ev_ring[1];
+	while (ev->event_cmd_cmpl.C != XHCI_INST (controller)->ev_ccs) {
+		debug("CRCR: %lx, USBSTS: %lx\n",  XHCI_INST (controller)->opreg->crcr_lo, XHCI_INST (controller)->opreg->usbsts);
+		debug("ev0.C %x, ev1.C %x\n", ev->event_cmd_cmpl.C, ev1->event_cmd_cmpl.C);
+		mdelay(100);
+	}
+	debug("command ring is %srunning\n", (XHCI_INST (controller)->opreg->crcr_lo & CRCR_CRR)?"":"not ");
+	switch (ev->event_cmd_cmpl.TRB_Type) {
+		case TRB_EV_CMD_CMPL:
+			debug("Completed command TRB at %lx. Code: %d\n",
+				ev->event_cmd_cmpl.Cmd_TRB_Pointer_lo, ev->event_cmd_cmpl.Completion_Code);
+			break;
+		case TRB_EV_PORTSC:
+			debug("Port Status Change Event. Completion Code: %d\n Port: %d. Ignoring.\n",
+				ev->event_cmd_cmpl.Completion_Code, ev->event_portsc.Port);
+			// we ignore the event as we look for the PORTSC registers instead, at a time when it suits _us_
+			break;
+		default:
+			debug("Unknown event: %d, Completion Code: %d\n", ev->event_cmd_cmpl.TRB_Type, ev->event_cmd_cmpl.Completion_Code);
+			break;
+	}
+	debug("CRCR: %lx, USBSTS: %lx\n",  XHCI_INST (controller)->opreg->crcr_lo, XHCI_INST (controller)->opreg->usbsts);
+	debug("ev0.C %x, ev1.C %x, ev1.CC %d\n", ev->event_cmd_cmpl.C, ev1->event_cmd_cmpl.C, ev1->event_cmd_cmpl.Completion_Code);
+
+	controller->devices[0]->controller = controller;
+	controller->devices[0]->init = xhci_rh_init;
+	controller->devices[0]->init (controller->devices[0]);
+
+	xhci_reset (controller);
+	return controller;
+}
+
+static void
+xhci_shutdown (hci_t *controller)
+{
+	if (controller == 0)
+		return;
+	detach_controller (controller);
+	XHCI_INST (controller)->roothub->destroy (XHCI_INST (controller)->
+						  roothub);
+	/* TODO: stop hardware, kill data structures */
+	free (XHCI_INST (controller));
+	free (controller);
+}
+
+static void
+xhci_start (hci_t *controller)
+{
+}
+
+static void
+xhci_stop (hci_t *controller)
+{
+}
+
+static int
+xhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
+	      unsigned char *data)
+{
+	return 1;
+}
+
+/* finalize == 1: if data is of packet aligned size, add a zero length packet */
+static int
+xhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
+{
+	int maxpsize = ep->maxpacketsize;
+	if (maxpsize == 0)
+		usb_fatal ("MaxPacketSize == 0!!!");
+	return 1;
+}
+
+/* create and hook-up an intr queue into device schedule */
+static void*
+xhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
+{
+	return NULL;
+}
+
+/* remove queue from device schedule, dropping all data that came in */
+static void
+xhci_destroy_intr_queue (endpoint_t *ep, void *q_)
+{
+	//free(q);
+}
+
+/* read one intr-packet from queue, if available. extend the queue for new input.
+   return NULL if nothing new available.
+   Recommended use: while (data=poll_intr_queue(q)) process(data);
+ */
+static u8*
+xhci_poll_intr_queue (void *q_)
+{
+	return NULL;
+}
Index: libpayload-release/drivers/usb/xhci.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/xhci.h	2010-08-12 15:53:07.395182800 +0200
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __XHCI_H
+#define __XHCI_H
+
+#include <pci.h>
+#include <usb/usb.h>
+
+     hci_t *xhci_init (pcidev_t addr);
+
+     void xhci_rh_init (usbdev_t *dev);
+
+#endif
Index: libpayload-release/drivers/usb/xhci_private.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/xhci_private.h	2010-08-12 15:53:47.549652800 +0200
@@ -0,0 +1,350 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __XHCI_PRIVATE_H
+#define __XHCI_PRIVATE_H
+
+#include <usb/usb.h>
+
+#define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit))
+
+typedef volatile union trb {
+	// transfer
+
+	// events
+#define TRB_EV_CMD_CMPL 33
+	struct {
+		u32 Cmd_TRB_Pointer_lo;
+		u32 Cmd_TRB_Pointer_hi;
+		struct {
+			unsigned long:24;
+			unsigned long Completion_Code:8;
+		} __attribute__ ((packed));
+		struct {
+			unsigned long C:1;
+			unsigned long:9;
+			unsigned long TRB_Type:6;
+			unsigned long VF_ID:8;
+			unsigned long Slot_ID:8;
+		} __attribute__ ((packed));
+	} __attribute__ ((packed)) event_cmd_cmpl;
+
+#define TRB_EV_PORTSC 34
+	struct {
+		struct {
+			unsigned long:24;
+			unsigned long Port:8;
+		} __attribute__ ((packed));
+		u32 rsvd;
+		struct {
+			unsigned long:24;
+			unsigned long Completion_Code:8;
+		} __attribute__ ((packed));
+		struct {
+			unsigned long C:1;
+			unsigned long:9;
+			unsigned long TRB_Type:6;
+			unsigned long:16;
+		} __attribute__ ((packed));
+	} __attribute__ ((packed)) event_portsc;
+
+	// commands
+#define TRB_CMD_NOOP 23
+	struct {
+		u32 rsvd[3];
+		struct {
+			unsigned long C:1;
+			unsigned long:9;
+			unsigned long TRB_Type:6;
+			unsigned long:16;
+		} __attribute__ ((packed));
+	} __attribute__ ((packed)) cmd_No_Op;
+
+	// "others"
+	struct {
+		u32 Ring_Segment_Ptr_lo;
+		u32 Ring_Segment_Ptr_hi;
+		struct {
+			unsigned long:22;
+			unsigned long Interrupter_Target;
+		} __attribute__ ((packed));
+		struct {
+			unsigned long C:1;
+			unsigned long TC:1;
+			unsigned long:2;
+			unsigned long CH:1;
+			unsigned long IOC:1;
+			unsigned long:4;
+			unsigned long TRB_Type:6;
+			unsigned long:16;
+		} __attribute__ ((packed));
+	} __attribute__ ((packed)) link;
+} trb_t;
+
+typedef struct slotctx {
+	struct {
+		unsigned long Route_String:20;
+		unsigned long Speed:4;
+		unsigned long:1;
+		unsigned long MTT:1;
+		unsigned long Hub:1;
+		unsigned long Context_Entries:5;
+	} __attribute__ ((packed));
+	struct {
+		unsigned long Max_Exit_Latency:16;
+		unsigned long Root_Hub_Port_Number:8;
+		unsigned long Number_of_Ports:8;
+	} __attribute__ ((packed));
+	struct {
+		unsigned long TT_Hub_Slot_ID:8;
+		unsigned long TT_Port_Number:8;
+		unsigned long TTT:2;
+		unsigned long:4;
+		unsigned long Interrupter_Target:10;
+	} __attribute__ ((packed));
+	struct {
+		unsigned long USB_Device_Address:8;
+		unsigned long:19;
+		unsigned long Slot_State:5;
+	} __attribute__ ((packed));
+	u32 rsvd[4];
+} slotctx_t;
+
+typedef struct epctx {
+	struct {
+		unsigned long EP_State:3;
+		unsigned long:5;
+		unsigned long Mult:2;
+		unsigned long MaxPStreams:5;
+		unsigned long LSA:1;
+		unsigned long Interval:8;
+		unsigned long:8;
+	} __attribute__ ((packed));
+	struct {
+		unsigned long:1;
+		unsigned long CErr:2;
+		unsigned long EP_Type:3;
+		unsigned long:1;
+		unsigned long HID:1;
+		unsigned long Max_Burst_Size:8;
+		unsigned long Max_Packet_Size:16;
+	} __attribute__ ((packed));
+	union {
+		u32 TR_Dequeue_Pointer_lo;
+		struct {
+			unsigned long DCS:1;
+			unsigned long:3;
+		} __attribute__ ((packed));
+	} __attribute__ ((packed));
+	u32 TR_Dequeue_Pointer_hi;
+	struct {
+		unsigned long Average_TRB_Length:16;
+		unsigned long Max_ESIT_Payload:16;
+	} __attribute__ ((packed));
+	u32 rsvd[3];
+} epctx_t;
+
+typedef struct devctx {
+	slotctx_t slot;
+	epctx_t ep0;
+	struct {
+		epctx_t out;
+		epctx_t in;
+	} eps[15];
+} devctx_t;
+
+typedef struct devctxp {
+	devctx_t *ptr;
+	void *upper;
+} devctxp_t;
+
+typedef struct erst_entry {
+	u32 seg_base_lo;
+	u32 seg_base_hi;
+	u32 seg_size;
+	u32 rsvd;
+} erst_entry_t;
+
+typedef struct xhci {
+	/* capreg is read-only, so no need for volatile,
+	   and thus 32bit accesses can be assumed. */
+	struct capreg {
+		u8 caplength;
+		u8 res1;
+		union {
+			u16 hciversion;
+			struct {
+				u8 hciver_lo;
+				u8 hciver_hi;
+			} __attribute__ ((packed));
+		} __attribute__ ((packed));
+		union {
+			u32 hcsparams1;
+			struct {
+				unsigned long MaxSlots:7;
+				unsigned long MaxIntrs:11;
+				unsigned long:6;
+				unsigned long MaxPorts:8;
+			} __attribute__ ((packed));
+		} __attribute__ ((packed));
+		union {
+			u32 hcsparams2;
+			struct {
+				unsigned long IST:4;
+				unsigned long ERST_Max:4;
+				unsigned long:18;
+				unsigned long SPR:1;
+				unsigned long Max_Scratchpad_Bufs:5;
+			} __attribute__ ((packed));
+		} __attribute__ ((packed));
+		union {
+			u32 hcsparams3;
+			struct {
+				unsigned long u1latency:8;
+				unsigned long:8;
+				unsigned long u2latency:16;
+			} __attribute__ ((packed));
+		} __attribute__ ((packed));
+		union {
+			u32 hccparams;
+			struct {
+				unsigned long ac64:1;
+				unsigned long bnc:1;
+				unsigned long csz:1;
+				unsigned long ppc:1;
+				unsigned long pind:1;
+				unsigned long lhrc:1;
+				unsigned long ltc:1;
+				unsigned long nss:1;
+				unsigned long:4;
+				unsigned long MaxPSASize:4;
+				unsigned long xECP:16;
+			} __attribute__ ((packed));
+		} __attribute__ ((packed));
+		u32 dboff;
+		u32 rtsoff;
+	} __attribute__ ((packed)) *capreg;
+
+	/* opreg is R/W is most places, so volatile access is necessary.
+	   volatile means that the compiler seeks byte writes if possible,
+	   making bitfields unusable for MMIO register blocks. Yay C :-( */
+	volatile struct opreg {
+		u32 usbcmd;
+#define USBCMD_RS 1<<0
+#define USBCMD_HCRST 1<<1
+		u32 usbsts;
+#define USBSTS_HCH 1<<0
+#define USBSTS_HSE 1<<2
+#define USBSTS_EINT 1<<3
+#define USBSTS_PCD 1<<4
+#define USBSTS_CNR 1<<11
+		u32 pagesize;
+		u8 res1[0x13-0x0c+1];
+		u32 dnctrl;
+		u32 crcr_lo;
+		u32 crcr_hi;
+#define CRCR_RCS 1<<0
+#define CRCR_CS 1<<1
+#define CRCR_CA 1<<2
+#define CRCR_CRR 1<<3
+		u8 res2[0x2f-0x20+1];
+		u32 dcbaap_lo;
+		u32 dcbaap_hi;
+		u32 config;
+#define CONFIG_MASK_MaxSlotsEn 0xff
+		u8 res3[0x3ff-0x3c+1];
+		struct {
+			u32 portsc;
+#define PORTSC_CCS 1<<0
+#define PORTSC_PED 1<<1
+	// BIT 2 rsvdZ
+#define PORTSC_OCA 1<<3
+#define PORTSC_PR 1<<4
+#define PORTSC_PLS 1<<5
+#define PORTSC_PLS_MASK MASK(5, 4)
+#define PORTSC_PP 1<<9
+#define PORTSC_PORT_SPEED 1<<10
+#define PORTSC_PORT_SPEED_MASK MASK(10, 4)
+#define PORTSC_PIC 1<<14
+#define PORTSC_PIC_MASK MASK(14, 2)
+#define PORTSC_LWS 1<<16
+#define PORTSC_CSC 1<<17
+#define PORTSC_PEC 1<<18
+#define PORTSC_WRC 1<<19
+#define PORTSC_OCC 1<<20
+#define PORTSC_PRC 1<<21
+#define PORTSC_PLC 1<<22
+#define PORTSC_CEC 1<<23
+#define PORTSC_CAS 1<<24
+#define PORTSC_WCE 1<<25
+#define PORTSC_WDE 1<<26
+#define PORTSC_WOE 1<<27
+	// BIT 29:28 rsvdZ
+#define PORTSC_DR 1<<30
+#define PORTSC_WPR 1<<31
+#define PORTSC_RW_MASK PORTSC_PR | PORTSC_PLS_MASK | PORTSC_PP | PORTSC_PIC_MASK | PORTSC_LWS | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE
+			u32 portpmsc;
+			u32 portli;
+			u32 res;
+		} __attribute__ ((packed)) prs[];
+	} __attribute__ ((packed)) *opreg;
+
+	/* R/W, volatile, MMIO -> no bitfields */
+	volatile struct hcrreg {
+		u32 mfindex;
+		u8 res1[0x20-0x4];
+		struct {
+			u32 iman;
+			u32 imod;
+			u32 erstsz;
+			u32 res;
+			u32 erstba_lo;
+			u32 erstba_hi;
+			u32 erdp_lo;
+			u32 erdp_hi;
+		} __attribute__ ((packed)) intrrs[]; // up to 1024, but maximum host specific, given in capreg->MaxIntrs
+	} __attribute__ ((packed)) *hcrreg;
+
+	/* R/W, volatile, MMIO -> no bitfields */
+	volatile u32 *dbreg;
+
+	/* R/W, volatile, Memory -> bitfields allowed */
+	volatile devctxp_t *dcbaa;
+
+	trb_t *cmd_ring;
+	trb_t *ev_ring;
+	volatile erst_entry_t *ev_ring_table;
+	int cmd_ccs, ev_ccs;
+
+	usbdev_t *roothub;
+} xhci_t;
+
+#define XHCI_INST(controller) ((xhci_t*)((controller)->instance))
+
+#endif
Index: libpayload-release/drivers/usb/xhci_rh.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libpayload-release/drivers/usb/xhci_rh.c	2010-08-12 15:53:25.335214100 +0200
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define USB_DEBUG
+
+#include <libpayload.h>
+#include "xhci_private.h"
+#include "xhci.h"
+
+typedef struct {
+	int numports;
+	int *port;
+} rh_inst_t;
+
+#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
+
+static void
+xhci_rh_enable_port (usbdev_t *dev, int port)
+{
+	// FIXME: check power situation?
+	// enable slot
+	// attach device context to slot
+	// address device
+}
+
+/* disable root hub */
+static void
+xhci_rh_disable_port (usbdev_t *dev, int port)
+{
+}
+
+static void
+xhci_rh_scanport (usbdev_t *dev, int port)
+{
+	// clear CSC
+	int val = XHCI_INST (dev->controller)->opreg->prs[port].portsc;
+	val &= PORTSC_RW_MASK;
+	val |= PORTSC_CSC;
+	XHCI_INST (dev->controller)->opreg->prs[port].portsc = val;
+
+	debug("device attach status on port %x: %x\n", port, XHCI_INST (dev->controller)->opreg->prs[port].portsc & PORTSC_CCS);
+}
+
+static int
+xhci_rh_report_port_changes (usbdev_t *dev)
+{
+	int i;
+	// no change
+	if (!(XHCI_INST (dev->controller)->opreg->usbsts & USBSTS_PCD))
+		return -1;
+
+	for (i = 0; i < RH_INST (dev)->numports; i++) {
+		if (XHCI_INST (dev->controller)->opreg->prs[i].portsc & PORTSC_CSC) {
+			debug("found connect status change on port %d\n", i);
+			return i;
+		}
+	}
+
+	return -1; // shouldn't ever happen
+}
+
+static void
+xhci_rh_destroy (usbdev_t *dev)
+{
+	int i;
+	for (i = 0; i < RH_INST (dev)->numports; i++)
+		xhci_rh_disable_port (dev, i);
+	free (RH_INST (dev));
+}
+
+static void
+xhci_rh_poll (usbdev_t *dev)
+{
+	int port;
+	while ((port = xhci_rh_report_port_changes (dev)) != -1)
+		xhci_rh_scanport (dev, port);
+}
+
+void
+xhci_rh_init (usbdev_t *dev)
+{
+	int i;
+
+	dev->destroy = xhci_rh_destroy;
+	dev->poll = xhci_rh_poll;
+
+	dev->data = malloc (sizeof (rh_inst_t));
+	if (!dev->data)
+		usb_fatal ("Not enough memory for XHCI RH.\n");
+
+	RH_INST (dev)->numports = XHCI_INST (dev->controller)->capreg->MaxPorts;
+	RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports);
+	debug("%d ports registered\n", RH_INST (dev)->numports);
+
+	for (i = 0; i < RH_INST (dev)->numports; i++) {
+		xhci_rh_enable_port (dev, i);
+		RH_INST (dev)->port[i] = -1;
+	}
+
+	/* we can set them here because a root hub _really_ shouldn't
+	   appear elsewhere */
+	dev->address = 0;
+	dev->hub = -1;
+	dev->port = -1;
+
+	debug("rh init done\n");
+}
Index: libpayload-release/drivers/usb/usbinit.c
===================================================================
--- libpayload-release.orig/drivers/usb/usbinit.c	2010-08-12 15:52:25.977110300 +0200
+++ libpayload-release/drivers/usb/usbinit.c	2010-08-12 15:53:07.426382800 +0200
@@ -32,7 +32,7 @@
 #include "uhci.h"
 #include "ohci.h"
 //#include "ehci.h"
-//#include "xhci.h"
+#include "xhci.h"
 #include <usb/usbdisk.h>
 
 /**
@@ -98,10 +98,9 @@
 
 		}
 		if (prog_if == 0x30) {
-			printf ("XHCI controller\n");
+			printf ("xHCI controller\n");
 #ifdef CONFIG_USB_XHCI
-			//xhci_init(addr);
-			printf ("Not supported.\n");
+			xhci_init(addr);
 #else
 			printf ("Not supported.\n");
 #endif
Peter Stuge - 2010-08-12 18:06:35
Patrick Georgi wrote:
> attached patches add support for OHCI (USB1.1 in the non-intel/via
> flavor) and xHCI (USB3) to the libpayload USB stack.
..
> Signed-off-by: Patrick Georgi <patrick@georgi-clan.de>

Good stuff!

Acked-by: Peter Stuge <peter@stuge.se>
Anders Jenbo - 2010-08-12 23:56:51
Good work sofare :)


tor, 12 08 2010 kl. 16:35 +0200, skrev Patrick Georgi:
> Hi,
> 
> attached patches add support for OHCI (USB1.1 in the non-intel/via
> flavor) and xHCI (USB3) to the libpayload USB stack.
> 
> The code is the result of my Google Summer of Code project of this year.
> A big "Thank You!" to Google for sponsoring this effort, and the Summer
> of Code coordinators at Google for managing it.
> 
> 0.improve-bus-scan:
> - Properly report the bus number to the user
> - Look if a device exists before iterating all its functions
> 
> 
> 1.ohci-driver:
> Interrupt transfers work fine with UHCI, but it seems not to be a good
> match for any other of the USB standards, so I'm looking at how to
> improve it.
> For this reason, the OHCI driver doesn't support interrupt transfers.
> 
> This limitation affects keyboard operation. Mass storage (thumb drives,
> etc) are tested.
> 
> 
> 2.xhci-driver:
> xHCI is detected and does "basic" initialization (meaning: it does more
> complex things than the entire UHCI driver)
> Unfortunately my xHCI hardware, the xHCI specification and/or my
> understanding of either can't quite agree, so the driver is still
> missing a couple of things and thus is not working yet.
> The only user-visible capability (beyond printing lots of weird stuff on
> the debug channel) is reliable attach/detach detection.
> 
> 
> The patches must be applied in order due to their overlapping changes to
> common files.
> 
> I'll continue to work on both drivers, but decided to post the code in
> the current form already, to prevent last-minute pressure before the
> Google Summer of Code deadline.
> 
> 
> Signed-off-by: Patrick Georgi <patrick@georgi-clan.de>
> -- 
> coreboot mailing list: coreboot@coreboot.org
> http://www.coreboot.org/mailman/listinfo/coreboot

Patch

Index: libpayload-release/drivers/usb/usbinit.c
===================================================================
--- libpayload-release.orig/drivers/usb/usbinit.c	2010-08-12 15:28:09.215162800 +0200
+++ libpayload-release/drivers/usb/usbinit.c	2010-08-12 15:51:22.455598000 +0200
@@ -68,7 +68,7 @@ 
 		pci_command |= PCI_COMMAND_MASTER;
 		pci_write_config32(addr, PCI_COMMAND, pci_command);
 
-		printf ("%02x:%02x.%x %04x:%04x.%d ", 0, dev, func,
+		printf ("%02x:%02x.%x %04x:%04x.%d ", bus, dev, func,
 			pciid >> 16, pciid & 0xFFFF, func);
 		if (prog_if == 0) {
 			printf ("UHCI controller\n");
@@ -128,8 +128,9 @@ 
 	 */
 	for (bus = 0; bus < 256; bus++)
 		for (dev = 0; dev < 32; dev++)
-			for (func = 7; func >= 0 ; func--)
-				usb_controller_initialize (bus, dev, func);
+			if (pci_read_config32 (PCI_DEV(bus, dev, 0), 8) >> 16 == 0x0c03)
+				for (func = 7; func >= 0 ; func--)
+					usb_controller_initialize (bus, dev, func);
 	usb_poll();
 	return 0;
 }