Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / drivers / usbohci.c
diff --git a/qemu/roms/openbios/drivers/usbohci.c b/qemu/roms/openbios/drivers/usbohci.c
new file mode 100644 (file)
index 0000000..774164b
--- /dev/null
@@ -0,0 +1,926 @@
+/*
+ * Driver for USB OHCI ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was 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_ED
+
+#include "config.h"
+#include <asm/io.h>
+#include <libopenbios/ofmem.h>
+#include "timer.h"
+#include "drivers/pci.h"
+#include "pci.h"
+#include <drivers/usb.h>
+#include "usbohci_private.h"
+#include "usbohci.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_process_done_queue(ohci_t *ohci, int spew_debug);
+
+#ifdef USB_DEBUG_ED
+static void
+dump_td (td_t *cur)
+{
+       usb_debug("+---------------------------------------------------+\n");
+       if (((__le32_to_cpu(cur->config) & (3UL << 19)) >> 19) == 0)
+               usb_debug("|..[SETUP]..........................................|\n");
+       else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 2)
+               usb_debug("|..[IN].............................................|\n");
+       else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 1)
+               usb_debug("|..[OUT]............................................|\n");
+       else
+               usb_debug("|..[]...............................................|\n");
+       usb_debug("|:|============ OHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(cur));
+       usb_debug("|:| ERRORS = [%ld] | CONFIG = [0x%08x] |        |:|\n",
+                 3 - ((__le32_to_cpu(cur->config) & (3UL << 26)) >> 26), __le32_to_cpu(cur->config));
+       usb_debug("|:+-----------------------------------------------+:|\n");
+       usb_debug("|:|   C   | Condition Code               |   [%02ld] |:|\n",
+                (__le32_to_cpu(cur->config) & (0xFUL << 28)) >> 28);
+       usb_debug("|:|   O   | Direction/PID                |    [%ld] |:|\n",
+                (__le32_to_cpu(cur->config) & (3UL << 19)) >> 19);
+       usb_debug("|:|   N   | Buffer Rounding              |    [%ld] |:|\n",
+                (__le32_to_cpu(cur->config) & (1UL << 18)) >> 18);
+       usb_debug("|:|   F   | Delay Intterrupt             |    [%ld] |:|\n",
+                (__le32_to_cpu(cur->config) & (7UL << 21)) >> 21);
+       usb_debug("|:|   I   | Data Toggle                  |    [%ld] |:|\n",
+                (__le32_to_cpu(cur->config) & (3UL << 24)) >> 24);
+       usb_debug("|:|   G   | Error Count                  |    [%ld] |:|\n",
+                (__le32_to_cpu(cur->config) & (3UL << 26)) >> 26);
+       usb_debug("|:+-----------------------------------------------+:|\n");
+       usb_debug("|:| Current Buffer Pointer         [0x%08x]   |:|\n", __le32_to_cpu(cur->current_buffer_pointer));
+       usb_debug("|:+-----------------------------------------------+:|\n");
+       usb_debug("|:| Next TD                        [0x%08x]   |:|\n", __le32_to_cpu(cur->next_td));
+       usb_debug("|:+-----------------------------------------------+:|\n");
+       usb_debug("|:| Current Buffer End             [0x%08x]   |:|\n", __le32_to_cpu(cur->buffer_end));
+       usb_debug("|:|-----------------------------------------------|:|\n");
+       usb_debug("|...................................................|\n");
+       usb_debug("+---------------------------------------------------+\n");
+}
+
+static void
+dump_ed (ed_t *cur)
+{
+       td_t *tmp_td = NULL;
+       usb_debug("+===================================================+\n");
+       usb_debug("| ############# OHCI ED at [0x%08lx] ########### |\n", virt_to_phys(cur));
+       usb_debug("+---------------------------------------------------+\n");
+       usb_debug("| Next Endpoint Descriptor       [0x%08lx]       |\n", __le32_to_cpu(cur->next_ed) & ~0xFUL);
+       usb_debug("+---------------------------------------------------+\n");
+       usb_debug("|        |               @ 0x%08x :             |\n", __le32_to_cpu(cur->config));
+       usb_debug("|   C    | Maximum Packet Length           | [%04ld] |\n",
+                ((__le32_to_cpu(cur->config) & (0x3fffUL << 16)) >> 16));
+       usb_debug("|   O    | Function Address                | [%04d] |\n",
+                __le32_to_cpu(cur->config) & 0x7F);
+       usb_debug("|   N    | Endpoint Number                 |   [%02ld] |\n",
+                (__le32_to_cpu(cur->config) & (0xFUL << 7)) >> 7);
+       usb_debug("|   F    | Endpoint Direction              |    [%ld] |\n",
+                ((__le32_to_cpu(cur->config) & (3UL << 11)) >> 11));
+       usb_debug("|   I    | Endpoint Speed                  |    [%ld] |\n",
+                ((__le32_to_cpu(cur->config) & (1UL << 13)) >> 13));
+       usb_debug("|   G    | Skip                            |    [%ld] |\n",
+                ((__le32_to_cpu(cur->config) & (1UL << 14)) >> 14));
+       usb_debug("|        | Format                          |    [%ld] |\n",
+                ((__le32_to_cpu(cur->config) & (1UL << 15)) >> 15));
+       usb_debug("+---------------------------------------------------+\n");
+       usb_debug("| TD Queue Tail Pointer          [0x%08lx]       |\n",
+                __le32_to_cpu(cur->tail_pointer) & ~0xFUL);
+       usb_debug("+---------------------------------------------------+\n");
+       usb_debug("| TD Queue Head Pointer          [0x%08lx]       |\n",
+                __le32_to_cpu(cur->head_pointer) & ~0xFUL);
+       usb_debug("| CarryToggleBit    [%d]          Halted   [%d]       |\n",
+                (u16)(__le32_to_cpu(cur->head_pointer) & 0x2UL)>>1, (u16)(__le32_to_cpu(cur->head_pointer) & 0x1UL));
+
+       tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(cur->head_pointer) & ~0xFUL));
+       if ((__le32_to_cpu(cur->head_pointer) & ~0xFUL) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) {
+               usb_debug("|:::::::::::::::::: OHCI TD CHAIN ::::::::::::::::::|\n");
+               while (virt_to_phys(tmp_td) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL))
+               {
+                       dump_td(tmp_td);
+                       tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(tmp_td->next_td) & ~0xFUL));
+               }
+               usb_debug("|:::::::::::::::: EOF OHCI TD CHAIN ::::::::::::::::|\n");
+               usb_debug("+---------------------------------------------------+\n");
+       } else {
+               usb_debug("+---------------------------------------------------+\n");
+       }
+}
+#endif
+
+static void
+ohci_reset (hci_t *controller)
+{
+       if (controller == NULL)
+               return;
+
+       OHCI_INST(controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset);
+       mdelay(2); /* wait 2ms */
+       OHCI_INST(controller)->opreg->HcControl = 0;
+       mdelay(10); /* wait 10ms */
+}
+
+static void
+ohci_reinit (hci_t *controller)
+{
+}
+
+hci_t *
+ohci_init (void *bar)
+{
+       int i;
+
+       hci_t *controller = new_controller ();
+
+       if (!controller) {
+               printk("Could not create USB controller instance.\n");
+               return NULL;
+        }
+
+       controller->instance = malloc (sizeof (ohci_t));
+       if(!controller->instance) {
+               printk("Not enough memory creating USB controller instance.\n");
+                return NULL;
+        }
+
+       controller->type = OHCI;
+
+       controller->start = ohci_start;
+       controller->stop = ohci_stop;
+       controller->reset = ohci_reset;
+       controller->init = ohci_reinit;
+       controller->shutdown = ohci_shutdown;
+       controller->bulk = ohci_bulk;
+       controller->control = ohci_control;
+       controller->set_address = generic_set_address;
+       controller->finish_device_config = NULL;
+       controller->destroy_device = NULL;
+       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->reg_base = (u32)(unsigned long)bar;
+       OHCI_INST (controller)->opreg = (opreg_t*)phys_to_virt(controller->reg_base);
+       usb_debug("OHCI Version %x.%x\n",
+                 (READ_OPREG(OHCI_INST(controller), HcRevision) >> 4) & 0xf,
+                 READ_OPREG(OHCI_INST(controller), HcRevision) & 0xf);
+
+       if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) == USBReset) {
+               /* cold boot */
+               OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~RemoteWakeupConnected);
+               OHCI_INST (controller)->opreg->HcFmInterval =
+                       __cpu_to_le32((11999 * FrameInterval) | ((((11999 - 210)*6)/7) * FSLargestDataPacket));
+               /* TODO: right value for PowerOnToPowerGoodTime ? */
+               OHCI_INST (controller)->opreg->HcRhDescriptorA =
+                       __cpu_to_le32(NoPowerSwitching | NoOverCurrentProtection | (10 * PowerOnToPowerGoodTime));
+               OHCI_INST (controller)->opreg->HcRhDescriptorB = __cpu_to_le32(0 * DeviceRemovable);
+               udelay(100); /* TODO: reset asserting according to USB spec */
+       } else if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) != USBOperational) {
+               OHCI_INST (controller)->opreg->HcControl =
+                       __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) & ~HostControllerFunctionalStateMask)
+                       | USBResume);
+               udelay(100); /* TODO: resume time according to USB spec */
+       }
+       int interval = OHCI_INST (controller)->opreg->HcFmInterval;
+
+       OHCI_INST (controller)->opreg->HcCommandStatus = __cpu_to_le32(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;
+       ofmem_posix_memalign((void **)&(OHCI_INST (controller)->hcca), 256, 256);
+       memset((void*)OHCI_INST (controller)->hcca, 0, 256);
+
+       usb_debug("HCCA addr %p\n", OHCI_INST(controller)->hcca);
+       /* Initialize interrupt table. */
+       u32 *const intr_table = OHCI_INST(controller)->hcca->HccaInterruptTable;
+       ed_t *const periodic_ed;
+        ofmem_posix_memalign((void **)&periodic_ed, sizeof(ed_t), sizeof(ed_t));
+       memset((void *)periodic_ed, 0, sizeof(*periodic_ed));
+       for (i = 0; i < 32; ++i)
+               intr_table[i] = __cpu_to_le32(virt_to_phys(periodic_ed));
+       OHCI_INST (controller)->periodic_ed = periodic_ed;
+
+       OHCI_INST (controller)->opreg->HcHCCA = __cpu_to_le32(virt_to_phys(OHCI_INST(controller)->hcca));
+       /* Make sure periodic schedule is enabled. */
+       OHCI_INST (controller)->opreg->HcControl |= __cpu_to_le32(PeriodicListEnable);
+       OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~IsochronousEnable); // unused by this driver
+       // disable everything, contrary to what OHCI spec says in 5.1.1.4, as we don't need IRQs
+       OHCI_INST (controller)->opreg->HcInterruptEnable = __cpu_to_le32(1<<31);
+       OHCI_INST (controller)->opreg->HcInterruptDisable = __cpu_to_le32(~(1<<31));
+       OHCI_INST (controller)->opreg->HcInterruptStatus = __cpu_to_le32(~0);
+       OHCI_INST (controller)->opreg->HcPeriodicStart =
+               __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcFmInterval) & FrameIntervalMask) / 10 * 9);
+       OHCI_INST (controller)->opreg->HcControl = __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl)
+                                                               & ~HostControllerFunctionalStateMask) | USBOperational);
+
+       mdelay(100);
+
+       controller->devices[0]->controller = controller;
+       controller->devices[0]->init = ohci_rh_init;
+       controller->devices[0]->init (controller->devices[0]);
+       return controller;
+}
+
+hci_t *
+ohci_pci_init (pci_addr addr)
+{
+       u32 reg_base;
+       uint16_t cmd;
+
+       cmd = pci_config_read16(addr, PCI_COMMAND);
+       cmd |= PCI_COMMAND_BUS_MASTER;
+       pci_config_write16(addr, PCI_COMMAND, cmd);
+
+       /* regarding OHCI spec, Appendix A, BAR_OHCI register description, Table A-4
+        * BASE ADDRESS only [31-12] bits. All other usually 0, but not all.
+        * OHCI mandates MMIO, so bit 0 is clear */
+       reg_base = pci_config_read32 (addr, PCI_BASE_ADDR_0) & 0xfffff000;
+
+       return ohci_init((void *)(unsigned long)reg_base);
+}
+
+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);
+       controller->reset (controller);
+       free ((void *)OHCI_INST (controller)->periodic_ed);
+       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 int
+wait_for_ed(usbdev_t *dev, ed_t *head, int pages)
+{
+       usb_debug("Waiting for %d pages on dev %p with head %p\n", pages, dev, head);
+       /* wait for results */
+       /* TOTEST: how long to wait?
+        *         give 2s per TD (2 pages) plus another 2s for now
+        */
+       int timeout = pages*1000 + 2000;
+       while (((__le32_to_cpu(head->head_pointer) & ~3) != __le32_to_cpu(head->tail_pointer)) &&
+               !(__le32_to_cpu(head->head_pointer) & 1) &&
+               ((__le32_to_cpu((((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3)))->config)
+               & TD_CC_MASK) >= TD_CC_NOACCESS) && timeout--) {
+               /* don't log every ms */
+               if (!(timeout % 100))
+               usb_debug("intst: %x; ctrl: %x; cmdst: %x; head: %x -> %x, tail: %x, condition: %x\n",
+                         READ_OPREG(OHCI_INST(dev->controller), HcInterruptStatus),
+                         READ_OPREG(OHCI_INST(dev->controller), HcControl),
+                         READ_OPREG(OHCI_INST(dev->controller), HcCommandStatus),
+                       __le32_to_cpu(head->head_pointer),
+                       __le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->next_td),
+                       __le32_to_cpu(head->tail_pointer),
+                       (__le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->config) & TD_CC_MASK) >> TD_CC_SHIFT);
+               mdelay(1);
+       }
+       if (timeout < 0)
+               usb_debug("Error: ohci: endpoint "
+                       "descriptor processing timed out.\n");
+       /* Clear the done queue. */
+       ohci_process_done_queue(OHCI_INST(dev->controller), 1);
+
+       if (__le32_to_cpu(head->head_pointer) & 1) {
+               usb_debug("HALTED!\n");
+               return 1;
+       }
+       return 0;
+}
+
+static void
+ohci_free_ed (ed_t *const head)
+{
+       /* In case the transfer canceled, we have to free unprocessed TDs. */
+       while ((__le32_to_cpu(head->head_pointer) & ~0x3) != __le32_to_cpu(head->tail_pointer)) {
+               /* Save current TD pointer. */
+               td_t *const cur_td =
+                       (td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3);
+               /* Advance head pointer. */
+               head->head_pointer = cur_td->next_td;
+               /* Free current TD. */
+               free((void *)cur_td);
+       }
+
+       /* Always free the dummy TD */
+       if ((__le32_to_cpu(head->head_pointer) & ~0x3) == __le32_to_cpu(head->tail_pointer))
+               free(phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3));
+       /* and the ED. */
+       free((void *)head);
+}
+
+static int
+ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
+             unsigned char *data)
+{
+       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);
+
+       /* First TD. */
+       td_t *const first_td;
+        ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t));
+       memset((void *)first_td, 0, sizeof(*first_td));
+       cur = first_td;
+
+       cur->config = __cpu_to_le32(TD_DIRECTION_SETUP |
+               TD_DELAY_INTERRUPT_NOINTR |
+               TD_TOGGLE_FROM_TD |
+               TD_TOGGLE_DATA0 |
+               TD_CC_NOACCESS);
+       cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(devreq));
+       cur->buffer_end = __cpu_to_le32(virt_to_phys((char *)devreq + drlen - 1));
+
+       while (pages > 0) {
+               /* One more TD. */
+               td_t *const next;
+               ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t));
+               memset((void *)next, 0, sizeof(*next));
+               /* Linked to the previous. */
+               cur->next_td = __cpu_to_le32(virt_to_phys(next));
+               /* Advance to the new TD. */
+               cur = next;
+
+               cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) |
+                       TD_DELAY_INTERRUPT_NOINTR |
+                       TD_TOGGLE_FROM_ED |
+                       TD_CC_NOACCESS);
+               cur->current_buffer_pointer = __cpu_to_le32(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 = __cpu_to_le32(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 = __cpu_to_le32(virt_to_phys(data + second_page_size - 1));
+                       dalen -= second_page_size;
+                       data += second_page_size;
+               }
+       }
+
+       /* One more TD. */
+       td_t *const next_td;
+       ofmem_posix_memalign((void **)&next_td, sizeof(td_t), sizeof(td_t));
+       memset((void *)next_td, 0, sizeof(*next_td));
+       /* Linked to the previous. */
+       cur->next_td = __cpu_to_le32(virt_to_phys(next_td));
+       /* Advance to the new TD. */
+       cur = next_td;
+       cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_OUT : TD_DIRECTION_IN) |
+               TD_DELAY_INTERRUPT_ZERO | /* Write done head after this TD. */
+               TD_TOGGLE_FROM_TD |
+               TD_TOGGLE_DATA1 |
+               TD_CC_NOACCESS);
+       cur->current_buffer_pointer = 0;
+       cur->buffer_end = 0;
+
+       /* Final dummy TD. */
+       td_t *const final_td;
+       ofmem_posix_memalign((void **)&final_td, sizeof(td_t), sizeof(td_t));
+       memset((void *)final_td, 0, sizeof(*final_td));
+       /* Linked to the previous. */
+       cur->next_td = __cpu_to_le32(virt_to_phys(final_td));
+
+       /* Data structures */
+       ed_t *head;
+       ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t));
+       memset((void*)head, 0, sizeof(*head));
+       head->config = __cpu_to_le32((dev->address << ED_FUNC_SHIFT) |
+               (0 << ED_EP_SHIFT) |
+               (OHCI_FROM_TD << ED_DIR_SHIFT) |
+               (dev->speed?ED_LOWSPEED:0) |
+               (dev->endpoints[0].maxpacketsize << ED_MPS_SHIFT));
+       head->tail_pointer = __cpu_to_le32(virt_to_phys(final_td));
+       head->head_pointer = __cpu_to_le32(virt_to_phys(first_td));
+
+       usb_debug("ohci_control(): doing transfer with %x. first_td at %x\n",
+               __le32_to_cpu(head->config) & ED_FUNC_MASK, __le32_to_cpu(head->head_pointer));
+#ifdef USB_DEBUG_ED
+       dump_ed(head);
+#endif
+
+       /* activate schedule */
+       OHCI_INST(dev->controller)->opreg->HcControlHeadED = __cpu_to_le32(virt_to_phys(head));
+       OHCI_INST(dev->controller)->opreg->HcControl |= __cpu_to_le32(ControlListEnable);
+       OHCI_INST(dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(ControlListFilled);
+
+       int failure = wait_for_ed(dev, head,
+                       (dalen==0)?0:(last_page - first_page + 1));
+       /* Wait some frames before and one after disabling list access. */
+       mdelay(4);
+       OHCI_INST(dev->controller)->opreg->HcControl &= __cpu_to_le32(~ControlListEnable);
+       mdelay(1);
+
+       /* free memory */
+       ohci_free_ed(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;
+       usb_debug("bulk: %x bytes from %p, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize);
+
+       td_t *cur, *next;
+
+       // 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++;
+       }
+
+       /* First TD. */
+       td_t *const first_td;
+       ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t));
+       memset((void *)first_td, 0, sizeof(*first_td));
+       cur = next = first_td;
+
+       for (i = 0; i < td_count; ++i) {
+               /* Advance to next TD. */
+               cur = next;
+               cur->config = __cpu_to_le32((ep->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) |
+                        TD_DELAY_INTERRUPT_NOINTR |
+                        TD_TOGGLE_FROM_ED |
+                        TD_CC_NOACCESS);
+               cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data));
+               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 = __cpu_to_le32(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 = __cpu_to_le32(virt_to_phys(data + second_page_size - 1));
+                       dalen -= second_page_size;
+                       data += second_page_size;
+               }
+               /* One more TD. */
+               ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t));
+               memset((void *)next, 0, sizeof(*next));
+               /* Linked to the previous. */
+               cur->next_td = __cpu_to_le32(virt_to_phys(next));
+       }
+
+       /* Write done head after last TD. */
+       cur->config &= __cpu_to_le32(~TD_DELAY_INTERRUPT_MASK);
+       /* Advance to final, dummy TD. */
+       cur = next;
+
+       /* Data structures */
+       ed_t *head;
+       ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t));
+       memset((void*)head, 0, sizeof(*head));
+       head->config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) |
+               ((ep->endpoint & 0xf) << ED_EP_SHIFT) |
+               (((ep->direction==IN)?OHCI_IN:OHCI_OUT) << ED_DIR_SHIFT) |
+               (ep->dev->speed?ED_LOWSPEED:0) |
+               (ep->maxpacketsize << ED_MPS_SHIFT));
+       head->tail_pointer = __cpu_to_le32(virt_to_phys(cur));
+       head->head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle?ED_TOGGLE:0));
+
+       usb_debug("doing bulk transfer with %x(%x). first_td at %lx, last %lx\n",
+               __le32_to_cpu(head->config) & ED_FUNC_MASK,
+               (__le32_to_cpu(head->config) & ED_EP_MASK) >> ED_EP_SHIFT,
+               virt_to_phys(first_td), virt_to_phys(cur));
+
+       /* activate schedule */
+       OHCI_INST(ep->dev->controller)->opreg->HcBulkHeadED = __cpu_to_le32(virt_to_phys(head));
+       OHCI_INST(ep->dev->controller)->opreg->HcControl |= __cpu_to_le32(BulkListEnable);
+       OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(BulkListFilled);
+
+       int failure = wait_for_ed(ep->dev, head,
+                       (dalen==0)?0:(last_page - first_page + 1));
+       /* Wait some frames before and one after disabling list access. */
+       mdelay(4);
+       OHCI_INST(ep->dev->controller)->opreg->HcControl &= __cpu_to_le32(~BulkListEnable);
+       mdelay(1);
+
+       ep->toggle = __le32_to_cpu(head->head_pointer) & ED_TOGGLE;
+
+       /* free memory */
+       ohci_free_ed(head);
+
+       if (failure) {
+               /* try cleanup */
+               clear_stall(ep);
+       }
+
+       return failure;
+}
+
+
+struct _intr_queue;
+
+struct _intrq_td {
+       volatile td_t           td;
+       u8                      *data;
+       struct _intrq_td        *next;
+       struct _intr_queue      *intrq;
+};
+
+struct _intr_queue {
+       volatile ed_t           ed;
+       struct _intrq_td        *head;
+       struct _intrq_td        *tail;
+       u8                      *data;
+       int                     reqsize;
+       endpoint_t              *endp;
+       unsigned int            remaining_tds;
+       int                     destroy;
+};
+
+typedef struct _intrq_td intrq_td_t;
+typedef struct _intr_queue intr_queue_t;
+
+#define INTRQ_TD_FROM_TD(x) ((intrq_td_t *)x)
+
+static void
+ohci_fill_intrq_td(intrq_td_t *const td, intr_queue_t *const intrq,
+                  u8 *const data)
+{
+       memset(td, 0, sizeof(*td));
+       td->td.config = __cpu_to_le32(TD_QUEUETYPE_INTR |
+               (intrq->endp->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) |
+               TD_DELAY_INTERRUPT_ZERO |
+               TD_TOGGLE_FROM_ED |
+               TD_CC_NOACCESS);
+       td->td.current_buffer_pointer = __cpu_to_le32(virt_to_phys(data));
+       td->td.buffer_end = __cpu_to_le32(virt_to_phys(data) + intrq->reqsize - 1);
+       td->intrq = intrq;
+       td->data = data;
+}
+
+/* create and hook-up an intr queue into device schedule */
+static void *
+ohci_create_intr_queue(endpoint_t *const ep, const int reqsize,
+                      const int reqcount, const int reqtiming)
+{
+       int i;
+       intrq_td_t *first_td = NULL, *last_td = NULL;
+
+       if (reqsize > 4096)
+               return NULL;
+
+       intr_queue_t *const intrq;
+       ofmem_posix_memalign((void **)&intrq, sizeof(intrq->ed), sizeof(*intrq));
+       memset(intrq, 0, sizeof(*intrq));
+       intrq->data = (u8 *)malloc(reqcount * reqsize);
+       intrq->reqsize = reqsize;
+       intrq->endp = ep;
+
+       /* Create #reqcount TDs. */
+       u8 *cur_data = intrq->data;
+       for (i = 0; i < reqcount; ++i) {
+               intrq_td_t *const td;
+               ofmem_posix_memalign((void **)&td, sizeof(td->td), sizeof(*td));
+               ++intrq->remaining_tds;
+               ohci_fill_intrq_td(td, intrq, cur_data);
+               cur_data += reqsize;
+               if (!first_td)
+                       first_td = td;
+               else
+                       last_td->td.next_td = __cpu_to_le32(virt_to_phys(&td->td));
+               last_td = td;
+       }
+
+       /* Create last, dummy TD. */
+       intrq_td_t *dummy_td;
+       ofmem_posix_memalign((void **)&dummy_td, sizeof(dummy_td->td), sizeof(*dummy_td));
+       memset(dummy_td, 0, sizeof(*dummy_td));
+       dummy_td->intrq = intrq;
+       if (last_td)
+               last_td->td.next_td = __cpu_to_le32(virt_to_phys(&dummy_td->td));
+       last_td = dummy_td;
+
+       /* Initialize ED. */
+       intrq->ed.config =  __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) |
+               ((ep->endpoint & 0xf) << ED_EP_SHIFT) |
+               (((ep->direction == IN) ? OHCI_IN : OHCI_OUT) << ED_DIR_SHIFT) |
+               (ep->dev->speed ? ED_LOWSPEED : 0) |
+               (ep->maxpacketsize << ED_MPS_SHIFT));
+       intrq->ed.tail_pointer = __cpu_to_le32(virt_to_phys(last_td));
+       intrq->ed.head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle ? ED_TOGGLE : 0));
+
+#ifdef USB_DEBUG_ED
+       dump_ed(&intrq->ed);
+#endif
+       /* Insert ED into periodic table. */
+       int nothing_placed      = 1;
+       ohci_t *const ohci      = OHCI_INST(ep->dev->controller);
+       u32 *const intr_table   = ohci->hcca->HccaInterruptTable;
+       const u32 dummy_ptr     = __cpu_to_le32(virt_to_phys(ohci->periodic_ed));
+       for (i = 0; i < 32; i += reqtiming) {
+               /* Advance to the next free position. */
+               while ((i < 32) && (intr_table[i] != dummy_ptr)) ++i;
+               if (i < 32) {
+                       usb_debug("Placed endpoint %lx to %d\n", virt_to_phys(&intrq->ed), i);
+                       intr_table[i] = __cpu_to_le32(virt_to_phys(&intrq->ed));
+                       nothing_placed = 0;
+               }
+       }
+       if (nothing_placed) {
+               usb_debug("Error: Failed to place ohci interrupt endpoint "
+                       "descriptor into periodic table: no space left\n");
+               ohci_destroy_intr_queue(ep, intrq);
+               return NULL;
+       }
+
+       return intrq;
+}
+
+/* remove queue from device schedule, dropping all data that came in */
+static void
+ohci_destroy_intr_queue(endpoint_t *const ep, void *const q_)
+{
+       intr_queue_t *const intrq = (intr_queue_t *)q_;
+
+       int i;
+
+       /* Remove interrupt queue from periodic table. */
+       ohci_t *const ohci      = OHCI_INST(ep->dev->controller);
+       u32 *const intr_table   = ohci->hcca->HccaInterruptTable;
+       for (i=0; i < 32; ++i) {
+               if (intr_table[i] == __cpu_to_le32(virt_to_phys(intrq)))
+                       intr_table[i] = __cpu_to_le32(virt_to_phys(ohci->periodic_ed));
+       }
+       /* Wait for frame to finish. */
+       mdelay(1);
+
+       /* Free unprocessed TDs. */
+       while ((__le32_to_cpu(intrq->ed.head_pointer) & ~0x3) != __le32_to_cpu(intrq->ed.tail_pointer)) {
+               td_t *const cur_td = (td_t *)phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3);
+               intrq->ed.head_pointer = cur_td->next_td;
+               free(INTRQ_TD_FROM_TD(cur_td));
+               --intrq->remaining_tds;
+       }
+       /* Free final, dummy TD. */
+       free(phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3));
+       /* Free data buffer. */
+       free(intrq->data);
+
+       /* Free TDs already fetched from the done queue. */
+       ohci_process_done_queue(ohci, 1);
+       while (intrq->head) {
+               intrq_td_t *const cur_td = (intrq_td_t *const )__le32_to_cpu(intrq->head);
+               intrq->head = intrq->head->next;
+               free(cur_td);
+               --intrq->remaining_tds;
+       }
+
+       /* Mark interrupt queue to be destroyed.
+          ohci_process_done_queue() will free the remaining TDs
+          and finish the interrupt queue off once all TDs are gone. */
+       intrq->destroy = 1;
+
+       /* Save data toggle. */
+       ep->toggle = __le32_to_cpu(intrq->ed.head_pointer) & ED_TOGGLE;
+}
+
+/* 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 *const q_)
+{
+       intr_queue_t *const intrq = (intr_queue_t *)q_;
+
+       u8 *data = NULL;
+
+       /* Process done queue first, then check if we have work to do. */
+       ohci_process_done_queue(OHCI_INST(intrq->endp->dev->controller), 0);
+
+       if (intrq->head) {
+               /* Save pointer to processed TD and advance. */
+               intrq_td_t *const cur_td = intrq->head;
+               intrq->head = cur_td->next;
+
+               /* Return data buffer of this TD. */
+               data = cur_td->data;
+
+               /* Requeue this TD (i.e. copy to dummy and requeue as dummy). */
+               intrq_td_t *const dummy_td =
+                       INTRQ_TD_FROM_TD(phys_to_virt(__le32_to_cpu(intrq->ed.tail_pointer)));
+               ohci_fill_intrq_td(dummy_td, intrq, data);
+               /* Reset all but intrq pointer (i.e. init as dummy). */
+               memset(cur_td, 0, sizeof(*cur_td));
+               cur_td->intrq = intrq;
+               /* Insert into interrupt queue as dummy. */
+               dummy_td->td.next_td = __le32_to_cpu(virt_to_phys(&cur_td->td));
+               intrq->ed.tail_pointer = __le32_to_cpu(virt_to_phys(&cur_td->td));
+       }
+
+       return data;
+}
+
+static void
+ohci_process_done_queue(ohci_t *const ohci, const int spew_debug)
+{
+       int i, j;
+
+       /* Temporary queue of interrupt queue TDs (to reverse order). */
+       intrq_td_t *temp_tdq = NULL;
+
+       /* Check if done head has been written. */
+       if (!(READ_OPREG(ohci, HcInterruptStatus) & WritebackDoneHead))
+               return;
+       /* Fetch current done head.
+          Lsb is only interesting for hw interrupts. */
+       u32 phys_done_queue = __le32_to_cpu(ohci->hcca->HccaDoneHead) & ~1;
+       /* Tell host controller, he may overwrite the done head pointer. */
+       ohci->opreg->HcInterruptStatus = __cpu_to_le32(WritebackDoneHead);
+
+       i = 0;
+       /* Process done queue (it's in reversed order). */
+       while (phys_done_queue) {
+               td_t *const done_td = (td_t *)phys_to_virt(phys_done_queue);
+
+               /* Advance pointer to next TD. */
+               phys_done_queue = __le32_to_cpu(done_td->next_td);
+
+               switch (__le32_to_cpu(done_td->config) & TD_QUEUETYPE_MASK) {
+               case TD_QUEUETYPE_ASYNC:
+                       /* Free processed async TDs. */
+                  free((void *)done_td);
+                       break;
+               case TD_QUEUETYPE_INTR: {
+                       intrq_td_t *const td = INTRQ_TD_FROM_TD(done_td);
+                       intr_queue_t *const intrq = td->intrq;
+                       /* Check if the corresponding interrupt
+                          queue is still beeing processed. */
+                       if (intrq->destroy) {
+                               /* Free this TD, and */
+                               free(td);
+                               --intrq->remaining_tds;
+                               /* the interrupt queue if it has no more TDs. */
+                               if (!intrq->remaining_tds)
+                                       free(intrq);
+                               usb_debug("Freed TD from orphaned interrupt "
+                                         "queue, %d TDs remain.\n",
+                                         intrq->remaining_tds);
+                       } else {
+                               /* Save done TD to be processed. */
+                               td->next = temp_tdq;
+                               temp_tdq = td;
+                       }
+                       break;
+               }
+               default:
+                       break;
+               }
+               ++i;
+       }
+       if (spew_debug)
+               usb_debug("Processed %d done TDs.\n", i);
+
+       j = 0;
+       /* Process interrupt queue TDs in right order. */
+       while (temp_tdq) {
+               /* Save pointer of current TD and advance. */
+               intrq_td_t *const cur_td = temp_tdq;
+               temp_tdq = temp_tdq->next;
+
+               /* The interrupt queue for the current TD. */
+               intr_queue_t *const intrq = cur_td->intrq;
+               /* Append to interrupt queue. */
+               if (!intrq->head) {
+                       /* First element. */
+                       intrq->head = intrq->tail = cur_td;
+               } else {
+                       /* Insert at tail. */
+                       intrq->tail->next = cur_td;
+                       intrq->tail = cur_td;
+               }
+               /* It's always the last element. */
+               cur_td->next = NULL;
+               ++j;
+       }
+       if (spew_debug)
+               usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j);
+}
+
+int ob_usb_ohci_init (const char *path, uint32_t addr)
+{
+       hci_t *ctrl;
+       int i;
+
+       usb_debug("ohci_init: %s addr = %x\n", path, addr);
+       ctrl = ohci_pci_init(addr);
+       if (!ctrl)
+               return 0;
+
+       /* Init ports */
+       usb_poll();
+
+       /* Look for a keyboard */
+       for (i = 0; i < 128; i++) {
+               if (ctrl->devices[i] && ctrl->devices[i]->configuration) {
+                       configuration_descriptor_t *cd;
+                       interface_descriptor_t *intf;
+
+                       cd = (configuration_descriptor_t *)ctrl->devices[i]->configuration;
+                       intf = (interface_descriptor_t *)(ctrl->devices[i]->configuration + cd->bLength);
+                       usb_debug("Device at port %d is class %d\n", i, intf->bInterfaceClass);
+                       if (intf->bInterfaceClass == hid_device &&
+                           intf->bInterfaceSubClass == hid_subclass_boot &&
+                           intf->bInterfaceProtocol == hid_boot_proto_keyboard ) {
+                               break;
+                       }
+               }
+       }
+       if ( i < 128 )
+               ob_usb_hid_add_keyboard(path);
+
+       return 1;
+}