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
