[coreboot] [commit] r5691 - in trunk/payloads/libpayload: . drivers drivers/usb

repository service svn at coreboot.org
Fri Aug 13 11:18:59 CEST 2010


Author: oxygene
Date: Fri Aug 13 11:18:58 2010
New Revision: 5691
URL: https://tracker.coreboot.org/trac/coreboot/changeset/5691

Log:
Add support for OHCI controllers and prelimiary support for xHCI (USB3) controllers.
Improve scanning for USB controllers.

Limitations:
- OHCI doesn't support interrupt transfers yet (ie. no keyboards)
- xHCI just does initialization and device attach/detach so far

Signed-off-by: Patrick Georgi <patrick at georgi-clan.de>
Acked-by: Peter Stuge <peter at stuge.se>

Added:
   trunk/payloads/libpayload/drivers/usb/ohci.c
   trunk/payloads/libpayload/drivers/usb/ohci.h
   trunk/payloads/libpayload/drivers/usb/ohci_private.h
   trunk/payloads/libpayload/drivers/usb/ohci_rh.c
   trunk/payloads/libpayload/drivers/usb/xhci.c
   trunk/payloads/libpayload/drivers/usb/xhci.h
   trunk/payloads/libpayload/drivers/usb/xhci_private.h
   trunk/payloads/libpayload/drivers/usb/xhci_rh.c
Modified:
   trunk/payloads/libpayload/Config.in
   trunk/payloads/libpayload/drivers/Makefile.inc
   trunk/payloads/libpayload/drivers/usb/usbinit.c

Modified: trunk/payloads/libpayload/Config.in
==============================================================================
--- trunk/payloads/libpayload/Config.in	Wed Aug 11 01:34:51 2010	(r5690)
+++ trunk/payloads/libpayload/Config.in	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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"
@@ -233,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

Modified: trunk/payloads/libpayload/drivers/Makefile.inc
==============================================================================
--- trunk/payloads/libpayload/drivers/Makefile.inc	Wed Aug 11 01:34:51 2010	(r5690)
+++ trunk/payloads/libpayload/drivers/Makefile.inc	Fri Aug 13 11:18:58 2010	(r5691)
@@ -60,6 +60,10 @@
 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_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
 

Added: trunk/payloads/libpayload/drivers/usb/ohci.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/ohci.c	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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;
+}
+

Added: trunk/payloads/libpayload/drivers/usb/ohci.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/ohci.h	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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

Added: trunk/payloads/libpayload/drivers/usb/ohci_private.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/ohci_private.h	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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

Added: trunk/payloads/libpayload/drivers/usb/ohci_rh.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/ohci_rh.c	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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");
+}

Modified: trunk/payloads/libpayload/drivers/usb/usbinit.c
==============================================================================
--- trunk/payloads/libpayload/drivers/usb/usbinit.c	Wed Aug 11 01:34:51 2010	(r5690)
+++ trunk/payloads/libpayload/drivers/usb/usbinit.c	Fri Aug 13 11:18:58 2010	(r5691)
@@ -30,9 +30,9 @@
 #include <libpayload-config.h>
 #include <usb/usb.h>
 #include "uhci.h"
-//#include "ohci.h"
+#include "ohci.h"
 //#include "ehci.h"
-//#include "xhci.h"
+#include "xhci.h"
 #include <usb/usbdisk.h>
 
 /**
@@ -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");
@@ -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
@@ -99,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
@@ -128,8 +126,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;
 }

Added: trunk/payloads/libpayload/drivers/usb/xhci.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/xhci.c	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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;
+}

Added: trunk/payloads/libpayload/drivers/usb/xhci.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/xhci.h	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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

Added: trunk/payloads/libpayload/drivers/usb/xhci_private.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/xhci_private.h	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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

Added: trunk/payloads/libpayload/drivers/usb/xhci_rh.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/payloads/libpayload/drivers/usb/xhci_rh.c	Fri Aug 13 11:18:58 2010	(r5691)
@@ -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");
+}




More information about the coreboot mailing list