summaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/drivers/usb')
-rw-r--r--payloads/libpayload/drivers/usb/ohci.c472
-rw-r--r--payloads/libpayload/drivers/usb/ohci.h40
-rw-r--r--payloads/libpayload/drivers/usb/ohci_private.h255
-rw-r--r--payloads/libpayload/drivers/usb/ohci_rh.c164
-rw-r--r--payloads/libpayload/drivers/usb/usbinit.c19
-rw-r--r--payloads/libpayload/drivers/usb/xhci.c262
-rw-r--r--payloads/libpayload/drivers/usb/xhci.h40
-rw-r--r--payloads/libpayload/drivers/usb/xhci_private.h350
-rw-r--r--payloads/libpayload/drivers/usb/xhci_rh.c133
9 files changed, 1725 insertions, 10 deletions
diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c
new file mode 100644
index 0000000000..94c19454cb
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ohci.c
@@ -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;
+}
+
diff --git a/payloads/libpayload/drivers/usb/ohci.h b/payloads/libpayload/drivers/usb/ohci.h
new file mode 100644
index 0000000000..f5011676a1
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ohci.h
@@ -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
diff --git a/payloads/libpayload/drivers/usb/ohci_private.h b/payloads/libpayload/drivers/usb/ohci_private.h
new file mode 100644
index 0000000000..a340be1f3a
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ohci_private.h
@@ -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
diff --git a/payloads/libpayload/drivers/usb/ohci_rh.c b/payloads/libpayload/drivers/usb/ohci_rh.c
new file mode 100644
index 0000000000..da74340abd
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/ohci_rh.c
@@ -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");
+}
diff --git a/payloads/libpayload/drivers/usb/usbinit.c b/payloads/libpayload/drivers/usb/usbinit.c
index adaba34602..9c8063a3cb 100644
--- a/payloads/libpayload/drivers/usb/usbinit.c
+++ b/payloads/libpayload/drivers/usb/usbinit.c
@@ -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 @@ usb_controller_initialize (int bus, int dev, int func)
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 @@ usb_controller_initialize (int bus, int dev, int func)
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 @@ usb_controller_initialize (int bus, int dev, int func)
}
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 @@ usb_initialize (void)
*/
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;
}
diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c
new file mode 100644
index 0000000000..0aca63de9d
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/xhci.c
@@ -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;
+}
diff --git a/payloads/libpayload/drivers/usb/xhci.h b/payloads/libpayload/drivers/usb/xhci.h
new file mode 100644
index 0000000000..b900b12200
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/xhci.h
@@ -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
diff --git a/payloads/libpayload/drivers/usb/xhci_private.h b/payloads/libpayload/drivers/usb/xhci_private.h
new file mode 100644
index 0000000000..16834f77cc
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/xhci_private.h
@@ -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
diff --git a/payloads/libpayload/drivers/usb/xhci_rh.c b/payloads/libpayload/drivers/usb/xhci_rh.c
new file mode 100644
index 0000000000..2817f04299
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/xhci_rh.c
@@ -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");
+}