Add support for OHCI controllers and prelimiary support for xHCI (USB3) controllers.
authorPatrick Georgi <patrick@georgi-clan.de>
Fri, 13 Aug 2010 09:18:58 +0000 (09:18 +0000)
committerPatrick Georgi <patrick.georgi@coresystems.de>
Fri, 13 Aug 2010 09:18:58 +0000 (09:18 +0000)
Improve scanning for USB controllers.

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

Signed-off-by: Patrick Georgi <patrick@georgi-clan.de>
Acked-by: Peter Stuge <peter@stuge.se>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5691 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1

payloads/libpayload/Config.in
payloads/libpayload/drivers/Makefile.inc
payloads/libpayload/drivers/usb/ohci.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/ohci.h [new file with mode: 0644]
payloads/libpayload/drivers/usb/ohci_private.h [new file with mode: 0644]
payloads/libpayload/drivers/usb/ohci_rh.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usbinit.c
payloads/libpayload/drivers/usb/xhci.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/xhci.h [new file with mode: 0644]
payloads/libpayload/drivers/usb/xhci_private.h [new file with mode: 0644]
payloads/libpayload/drivers/usb/xhci_rh.c [new file with mode: 0644]

index 0cd0438fe463ceba85923622ce6523a2639c60de..5ac35faeca37f20943050d430ce5e103aba693ff 100644 (file)
@@ -224,7 +224,6 @@ config USB_OHCI
        help
          Select this option if you are going to use USB 1.1 on an AMD based
          system.
-         NOTE: This option is not (fully) implemented yet
 
 config USB_EHCI
        bool "Support for USB EHCI controllers"
@@ -233,6 +232,13 @@ config USB_EHCI
          Select this option if you want to use USB 2.0
          NOTE: This option is not (fully) implemented yet
 
+config USB_XHCI
+       bool "Support for USB xHCI controllers"
+       depends on USB
+       help
+         Select this option if you want to use USB 3.0
+         NOTE: This option is not (fully) implemented yet
+
 config USB_HID
        bool "Support for USB keyboards"
        depends on USB
index 0c7a4bc9050f818cccb77c8f865f565f6aa40661..134119fce4221d87033a34600c03f89e4851a66c 100644 (file)
@@ -60,6 +60,10 @@ TARGETS-$(CONFIG_USB) += drivers/usb/quirks.o
 TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o
 TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o
 TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o
+TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci.o
+TARGETS-$(CONFIG_USB_OHCI) += drivers/usb/ohci_rh.o
+TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci.o
+TARGETS-$(CONFIG_USB_XHCI) += drivers/usb/xhci_rh.o
 TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o
 TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o
 
diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c
new file mode 100644 (file)
index 0000000..94c1945
--- /dev/null
@@ -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 (file)
index 0000000..f501167
--- /dev/null
@@ -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 (file)
index 0000000..a340be1
--- /dev/null
@@ -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 (file)
index 0000000..da74340
--- /dev/null
@@ -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 adaba346029a5e3dea71c4fc9a8591dd9116e67d..9c8063a3cb17fcf4dda5c5a07f571765481920ea 100644 (file)
@@ -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 (file)
index 0000000..0aca63d
--- /dev/null
@@ -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 (file)
index 0000000..b900b12
--- /dev/null
@@ -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 (file)
index 0000000..16834f7
--- /dev/null
@@ -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 (file)
index 0000000..2817f04
--- /dev/null
@@ -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");
+}