]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/usb/host/ohci-hcd.c
usb: ohci: enable cache support
[karo-tx-uboot.git] / drivers / usb / host / ohci-hcd.c
index c33c487ee5438c8729d754ee14437ee12d46c716..0ffd838db29e2535ba064f73b5add9973af8d240 100644 (file)
@@ -30,6 +30,8 @@
 
 #include <common.h>
 #include <asm/byteorder.h>
+#include <dm.h>
+#include <errno.h>
 
 #if defined(CONFIG_PCI_OHCI)
 # include <pci.h>
@@ -47,7 +49,7 @@
 #include <asm/arch/hardware.h> /* needed for AT91_USB_HOST_BASE */
 #endif
 
-#if defined(CONFIG_ARM920T) || \
+#if defined(CONFIG_CPU_ARM920T) || \
     defined(CONFIG_S3C24X0) || \
     defined(CONFIG_440EP) || \
     defined(CONFIG_PCI_OHCI) || \
@@ -65,9 +67,6 @@
 #define OHCI_CONTROL_INIT \
        (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
 
-#define min_t(type, x, y) \
-                   ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
-
 #ifdef CONFIG_PCI_OHCI
 static struct pci_device_id ohci_pci_ids[] = {
        {0x10b9, 0x5237},       /* ULI1575 PCI OHCI module ids */
@@ -106,16 +105,96 @@ static struct pci_device_id ehci_pci_ids[] = {
 # define m32_swap(x) cpu_to_le32(x)
 #endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */
 
+/* We really should do proper cache flushing everywhere */
+#define flush_dcache_buffer(addr, size) \
+       flush_dcache_range((unsigned long)(addr), \
+               ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
+#define invalidate_dcache_buffer(addr, size) \
+       invalidate_dcache_range((unsigned long)(addr), \
+               ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
+
+/* Do not use sizeof(ed / td) as our ed / td structs contain extra members */
+#define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16)
+#define flush_dcache_td(addr) flush_dcache_buffer(addr, 16)
+#define flush_dcache_iso_td(addr) flush_dcache_buffer(addr, 32)
+#define flush_dcache_hcca(addr) flush_dcache_buffer(addr, 256)
+#define invalidate_dcache_ed(addr) invalidate_dcache_buffer(addr, 16)
+#define invalidate_dcache_td(addr) invalidate_dcache_buffer(addr, 16)
+#define invalidate_dcache_iso_td(addr) invalidate_dcache_buffer(addr, 32)
+#define invalidate_dcache_hcca(addr) invalidate_dcache_buffer(addr, 256)
+
+#ifdef CONFIG_DM_USB
+/*
+ * The various ohci_mdelay(1) calls in the code seem unnecessary. We keep
+ * them around when building for older boards not yet converted to the dm
+ * just in case (to avoid regressions), for dm this turns them into nops.
+ */
+#define ohci_mdelay(x)
+#else
+#define ohci_mdelay(x) mdelay(x)
+#endif
+
+#ifndef CONFIG_DM_USB
 /* global ohci_t */
 static ohci_t gohci;
 /* this must be aligned to a 256 byte boundary */
 struct ohci_hcca ghcca[1];
-/* a pointer to the aligned storage */
-struct ohci_hcca *phcca;
-/* this allocates EDs for all possible endpoints */
-struct ohci_device ohci_dev;
-/* device which was disconnected */
-struct usb_device *devgone;
+#endif
+
+/* mapping of the OHCI CC status to error codes */
+static int cc_to_error[16] = {
+       /* No  Error  */               0,
+       /* CRC Error  */               USB_ST_CRC_ERR,
+       /* Bit Stuff  */               USB_ST_BIT_ERR,
+       /* Data Togg  */               USB_ST_CRC_ERR,
+       /* Stall      */               USB_ST_STALLED,
+       /* DevNotResp */               -1,
+       /* PIDCheck   */               USB_ST_BIT_ERR,
+       /* UnExpPID   */               USB_ST_BIT_ERR,
+       /* DataOver   */               USB_ST_BUF_ERR,
+       /* DataUnder  */               USB_ST_BUF_ERR,
+       /* reservd    */               -1,
+       /* reservd    */               -1,
+       /* BufferOver */               USB_ST_BUF_ERR,
+       /* BuffUnder  */               USB_ST_BUF_ERR,
+       /* Not Access */               -1,
+       /* Not Access */               -1
+};
+
+static const char *cc_to_string[16] = {
+       "No Error",
+       "CRC: Last data packet from endpoint contained a CRC error.",
+       "BITSTUFFING: Last data packet from endpoint contained a bit " \
+                    "stuffing violation",
+       "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \
+                    "that did not match the expected value.",
+       "STALL: TD was moved to the Done Queue because the endpoint returned" \
+                    " a STALL PID",
+       "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \
+                    "not provide a handshake (OUT)",
+       "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\
+                    "(IN) or handshake (OUT)",
+       "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \
+                    "value is not defined.",
+       "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \
+                    "either the size of the maximum data packet allowed\n" \
+                    "from the endpoint (found in MaximumPacketSize field\n" \
+                    "of ED) or the remaining buffer size.",
+       "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \
+                    "and that amount was not sufficient to fill the\n" \
+                    "specified buffer",
+       "reserved1",
+       "reserved2",
+       "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \
+                    "than it could be written to system memory",
+       "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \
+                    "system memory fast enough to keep up with data USB " \
+                    "data rate.",
+       "NOT ACCESSED: This code is set by software before the TD is placed" \
+                    "on a list to be processed by the HC.(1)",
+       "NOT ACCESSED: This code is set by software before the TD is placed" \
+                    "on a list to be processed by the HC.(2)",
+};
 
 static inline u32 roothub_a(struct ohci *hc)
        { return ohci_readl(&hc->regs->roothub.a); }
@@ -127,11 +206,42 @@ static inline u32 roothub_portstatus(struct ohci *hc, int i)
        { return ohci_readl(&hc->regs->roothub.portstatus[i]); }
 
 /* forward declaration */
-static int hc_interrupt(void);
-static void td_submit_job(struct usb_device *dev, unsigned long pipe,
-                         void *buffer, int transfer_len,
+static int hc_interrupt(ohci_t *ohci);
+static void td_submit_job(ohci_t *ohci, struct usb_device *dev,
+                         unsigned long pipe, void *buffer, int transfer_len,
                          struct devrequest *setup, urb_priv_t *urb,
                          int interval);
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev,
+                      unsigned long pipe, int interval, int load);
+
+/*-------------------------------------------------------------------------*/
+
+/* TDs ... */
+static struct td *td_alloc(ohci_dev_t *ohci_dev, struct usb_device *usb_dev)
+{
+       int i;
+       struct td *td;
+
+       td = NULL;
+       for (i = 0; i < NUM_TD; i++)
+       {
+               if (ohci_dev->tds[i].usb_dev == NULL)
+               {
+                       td = &ohci_dev->tds[i];
+                       td->usb_dev = usb_dev;
+                       break;
+               }
+       }
+
+       return td;
+}
+
+static inline void ed_free(struct ed *ed)
+{
+       ed->usb_dev = NULL;
+}
 
 /*-------------------------------------------------------------------------*
  * URB support functions
@@ -161,18 +271,18 @@ static void urb_free_priv(urb_priv_t *urb)
 /*-------------------------------------------------------------------------*/
 
 #ifdef DEBUG
-static int sohci_get_current_frame_number(struct usb_device *dev);
+static int sohci_get_current_frame_number(ohci_t *ohci);
 
 /* debug| print the main components of an URB
  * small: 0) header + data packets 1) just header */
 
-static void pkt_print(urb_priv_t *purb, struct usb_device *dev,
+static void pkt_print(ohci_t *ohci, urb_priv_t *purb, struct usb_device *dev,
                      unsigned long pipe, void *buffer, int transfer_len,
                      struct devrequest *setup, char *str, int small)
 {
        dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx",
                        str,
-                       sohci_get_current_frame_number(dev),
+                       sohci_get_current_frame_number(ohci),
                        usb_pipedevice(pipe),
                        usb_pipeendpoint(pipe),
                        usb_pipeout(pipe)? 'O': 'I',
@@ -216,9 +326,11 @@ void ep_print_int_eds(ohci_t *ohci, char *str)
                ed_p = &(ohci->hcca->int_table [i]);
                if (*ed_p == 0)
                    continue;
+               invalidate_dcache_ed(ed_p);
                printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i);
                while (*ed_p != 0 && j--) {
                        ed_t *ed = (ed_t *)m32_swap(ed_p);
+                       invalidate_dcache_ed(ed);
                        printf(" ed: %4x;", ed->hwINFO);
                        ed_p = &ed->hwNextED;
                }
@@ -249,6 +361,7 @@ static void maybe_print_eds(char *label, __u32 value)
 
        if (value) {
                dbg("%s %08x", label, value);
+               invalidate_dcache_ed(edp);
                dbg("%08x", edp->hwINFO);
                dbg("%08x", edp->hwTailP);
                dbg("%08x", edp->hwHeadP);
@@ -383,6 +496,7 @@ static void ohci_dump(ohci_t *controller, int verbose)
        ohci_dump_status(controller);
        if (verbose)
                ep_print_int_eds(controller, "hcca");
+       invalidate_dcache_hcca(controller->hcca);
        dbg("hcca frame #%04x", controller->hcca->frame_no);
        ohci_dump_roothub(controller, 1);
 }
@@ -394,9 +508,9 @@ static void ohci_dump(ohci_t *controller, int verbose)
 
 /* get a transfer request */
 
-int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
+int sohci_submit_job(ohci_t *ohci, ohci_dev_t *ohci_dev, urb_priv_t *urb,
+                    struct devrequest *setup)
 {
-       ohci_t *ohci;
        ed_t *ed;
        urb_priv_t *purb_priv = urb;
        int i, size = 0;
@@ -406,8 +520,6 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
        int transfer_len = urb->transfer_buffer_length;
        int interval = urb->interval;
 
-       ohci = &gohci;
-
        /* when controller's hung, permit only roothub cleanup attempts
         * such as powering down ports */
        if (ohci->disabled) {
@@ -420,7 +532,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
        urb->finished = 0;
 
        /* every endpoint has a ed, locate and fill it */
-       ed = ep_add_ed(dev, pipe, interval, 1);
+       ed = ep_add_ed(ohci_dev, dev, pipe, interval, 1);
        if (!ed) {
                err("sohci_submit_job: ENOMEM");
                return -1;
@@ -456,7 +568,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
        /* allocate the TDs */
        /* note that td[0] was allocated in ep_add_ed */
        for (i = 0; i < size; i++) {
-               purb_priv->td[i] = td_alloc(dev);
+               purb_priv->td[i] = td_alloc(ohci_dev, dev);
                if (!purb_priv->td[i]) {
                        purb_priv->length = i;
                        urb_free_priv(purb_priv);
@@ -476,55 +588,19 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
                ep_link(ohci, ed);
 
        /* fill the TDs and link it to the ed */
-       td_submit_job(dev, pipe, buffer, transfer_len,
+       td_submit_job(ohci, dev, pipe, buffer, transfer_len,
                      setup, purb_priv, interval);
 
        return 0;
 }
 
-static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb)
-{
-       struct ohci_regs *regs = hc->regs;
-
-       switch (usb_pipetype(urb->pipe)) {
-       case PIPE_INTERRUPT:
-               /* implicitly requeued */
-               if (urb->dev->irq_handle &&
-                               (urb->dev->irq_act_len = urb->actual_length)) {
-                       ohci_writel(OHCI_INTR_WDH, &regs->intrenable);
-                       ohci_readl(&regs->intrenable); /* PCI posting flush */
-                       urb->dev->irq_handle(urb->dev);
-                       ohci_writel(OHCI_INTR_WDH, &regs->intrdisable);
-                       ohci_readl(&regs->intrdisable); /* PCI posting flush */
-               }
-               urb->actual_length = 0;
-               td_submit_job(
-                               urb->dev,
-                               urb->pipe,
-                               urb->transfer_buffer,
-                               urb->transfer_buffer_length,
-                               NULL,
-                               urb,
-                               urb->interval);
-               break;
-       case PIPE_CONTROL:
-       case PIPE_BULK:
-               break;
-       default:
-               return 0;
-       }
-       return 1;
-}
-
 /*-------------------------------------------------------------------------*/
 
 #ifdef DEBUG
 /* tell us the current USB frame number */
-
-static int sohci_get_current_frame_number(struct usb_device *usb_dev)
+static int sohci_get_current_frame_number(ohci_t *ohci)
 {
-       ohci_t *ohci = &gohci;
-
+       invalidate_dcache_hcca(ohci->hcca);
        return m16_swap(ohci->hcca->frame_no);
 }
 #endif
@@ -603,6 +679,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
        switch (ed->type) {
        case PIPE_CONTROL:
                ed->hwNextED = 0;
+               flush_dcache_ed(ed);
                if (ohci->ed_controltail == NULL)
                        ohci_writel(ed, &ohci->regs->ed_controlhead);
                else
@@ -620,6 +697,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
 
        case PIPE_BULK:
                ed->hwNextED = 0;
+               flush_dcache_ed(ed);
                if (ohci->ed_bulktail == NULL)
                        ohci_writel(ed, &ohci->regs->ed_bulkhead);
                else
@@ -652,7 +730,9 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
                                        inter = ep_rev(6,
                                                 ((ed_t *)ed_p)->int_interval);
                        ed->hwNextED = *ed_p;
+                       flush_dcache_ed(ed);
                        *ed_p = m32_swap((unsigned long)ed);
+                       flush_dcache_hcca(ohci->hcca);
                }
                break;
        }
@@ -665,6 +745,8 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
 static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed,
                            unsigned index, unsigned period)
 {
+       __maybe_unused unsigned long aligned_ed_p;
+
        for (; index < NUM_INTS; index += period) {
                __u32   *ed_p = &ohci->hcca->int_table [index];
 
@@ -673,6 +755,10 @@ static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed,
                        if (((struct ed *)
                                        m32_swap((unsigned long)ed_p)) == ed) {
                                *ed_p = ed->hwNextED;
+                               aligned_ed_p = (unsigned long)ed_p;
+                               aligned_ed_p &= ~(ARCH_DMA_MINALIGN - 1);
+                               flush_dcache_range(aligned_ed_p,
+                                       aligned_ed_p + ARCH_DMA_MINALIGN);
                                break;
                        }
                        ed_p = &(((struct ed *)
@@ -692,6 +778,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
        int i;
 
        ed->hwINFO |= m32_swap(OHCI_ED_SKIP);
+       flush_dcache_ed(ed);
 
        switch (ed->type) {
        case PIPE_CONTROL:
@@ -705,6 +792,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
                                &ohci->regs->ed_controlhead);
                } else {
                        ed->ed_prev->hwNextED = ed->hwNextED;
+                       flush_dcache_ed(ed->ed_prev);
                }
                if (ohci->ed_controltail == ed) {
                        ohci->ed_controltail = ed->ed_prev;
@@ -725,6 +813,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
                               &ohci->regs->ed_bulkhead);
                } else {
                        ed->ed_prev->hwNextED = ed->hwNextED;
+                       flush_dcache_ed(ed->ed_prev);
                }
                if (ohci->ed_bulktail == ed) {
                        ohci->ed_bulktail = ed->ed_prev;
@@ -754,14 +843,14 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
  * info fields are setted anyway even though most of them should not
  * change
  */
-static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe,
-                       int interval, int load)
+static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev,
+                      unsigned long pipe, int interval, int load)
 {
        td_t *td;
        ed_t *ed_ret;
        volatile ed_t *ed;
 
-       ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) |
+       ed = ed_ret = &ohci_dev->ed[(usb_pipeendpoint(pipe) << 1) |
                        (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))];
 
        if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
@@ -772,12 +861,12 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe,
 
        if (ed->state == ED_NEW) {
                /* dummy td; end of td list for ed */
-               td = td_alloc(usb_dev);
+               td = td_alloc(ohci_dev, usb_dev);
                ed->hwTailP = m32_swap((unsigned long)td);
                ed->hwHeadP = ed->hwTailP;
                ed->state = ED_UNLINK;
                ed->type = usb_pipetype(pipe);
-               ohci_dev.ed_cnt++;
+               ohci_dev->ed_cnt++;
        }
 
        ed->hwINFO = m32_swap(usb_pipedevice(pipe)
@@ -793,6 +882,8 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe,
                ed->int_load = load;
        }
 
+       flush_dcache_ed(ed);
+
        return ed_ret;
 }
 
@@ -818,6 +909,7 @@ static void td_fill(ohci_t *ohci, unsigned int info,
        /* use this td as the next dummy */
        td_pt = urb_priv->td [index];
        td_pt->hwNextTD = 0;
+       flush_dcache_td(td_pt);
 
        /* fill the old dummy TD */
        td = urb_priv->td [index] =
@@ -845,27 +937,30 @@ static void td_fill(ohci_t *ohci, unsigned int info,
                td->hwBE = 0;
 
        td->hwNextTD = m32_swap((unsigned long)td_pt);
+       flush_dcache_td(td);
 
        /* append to queue */
        td->ed->hwTailP = td->hwNextTD;
+       flush_dcache_ed(td->ed);
 }
 
 /*-------------------------------------------------------------------------*/
 
 /* prepare all TDs of a transfer */
 
-static void td_submit_job(struct usb_device *dev, unsigned long pipe,
-                         void *buffer, int transfer_len,
+static void td_submit_job(ohci_t *ohci, struct usb_device *dev,
+                         unsigned long pipe, void *buffer, int transfer_len,
                          struct devrequest *setup, urb_priv_t *urb,
                          int interval)
 {
-       ohci_t *ohci = &gohci;
        int data_len = transfer_len;
        void *data;
        int cnt = 0;
        __u32 info = 0;
        unsigned int toggle = 0;
 
+       flush_dcache_buffer(buffer, data_len);
+
        /* OHCI handles the DATA-toggles itself, we just use the USB-toggle
         * bits for reseting */
        if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
@@ -905,6 +1000,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe,
        case PIPE_CONTROL:
                /* Setup phase */
                info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+               flush_dcache_buffer(setup, 8);
                td_fill(ohci, info, setup, 8, dev, cnt++, urb);
 
                /* Optional Data phase */
@@ -917,7 +1013,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe,
                }
 
                /* Status phase */
-               info = usb_pipeout(pipe)?
+               info = (usb_pipeout(pipe) || data_len == 0) ?
                        TD_CC | TD_DP_IN | TD_T_DATA1:
                        TD_CC | TD_DP_OUT | TD_T_DATA1;
                td_fill(ohci, info, data, 0, dev, cnt++, urb);
@@ -976,6 +1072,7 @@ static void check_status(td_t *td_list)
        if (cc) {
                err(" USB-error: %s (%x)", cc_to_string[cc], cc);
 
+               invalidate_dcache_ed(td_list->ed);
                if (*phwHeadP & m32_swap(0x1)) {
                        if (lurb_priv &&
                            ((td_list->index + 1) < urb_len)) {
@@ -988,9 +1085,11 @@ static void check_status(td_t *td_list)
                                                     td_list->index - 1;
                        } else
                                *phwHeadP &= m32_swap(0xfffffff2);
+                       flush_dcache_ed(td_list->ed);
                }
 #ifdef CONFIG_MPC5200
                td_list->hwNextTD = 0;
+               flush_dcache_td(td_list);
 #endif
        }
 }
@@ -1003,11 +1102,14 @@ static td_t *dl_reverse_done_list(ohci_t *ohci)
        td_t *td_rev = NULL;
        td_t *td_list = NULL;
 
+       invalidate_dcache_hcca(ohci->hcca);
        td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0;
        ohci->hcca->done_head = 0;
+       flush_dcache_hcca(ohci->hcca);
 
        while (td_list_hc) {
                td_list = (td_t *)td_list_hc;
+               invalidate_dcache_td(td_list);
                check_status(td_list);
                td_list->next_dl_td = td_rev;
                td_rev = td_list;
@@ -1022,7 +1124,7 @@ static td_t *dl_reverse_done_list(ohci_t *ohci)
 static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status)
 {
        if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL))
-               urb->finished = sohci_return_job(ohci, urb);
+               urb->finished = 1;
        else
                dbg("finish_urb: strange.., ED state %x, \n", status);
 }
@@ -1042,6 +1144,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list)
        urb_priv_t *lurb_priv;
        __u32 tdINFO, edHeadP, edTailP;
 
+       invalidate_dcache_td(td_list);
        tdINFO = m32_swap(td_list->hwINFO);
 
        ed = td_list->ed;
@@ -1067,6 +1170,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list)
                lurb_priv->td_cnt, lurb_priv->length);
 
        if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) {
+               invalidate_dcache_ed(ed);
                edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0;
                edTailP = m32_swap(ed->hwTailP);
 
@@ -1094,103 +1198,7 @@ static int dl_done_list(ohci_t *ohci)
  * Virtual Root Hub
  *-------------------------------------------------------------------------*/
 
-/* Device descriptor */
-static __u8 root_hub_dev_des[] =
-{
-       0x12,       /*  __u8  bLength; */
-       0x01,       /*  __u8  bDescriptorType; Device */
-       0x10,       /*  __u16 bcdUSB; v1.1 */
-       0x01,
-       0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
-       0x00,       /*  __u8  bDeviceSubClass; */
-       0x00,       /*  __u8  bDeviceProtocol; */
-       0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
-       0x00,       /*  __u16 idVendor; */
-       0x00,
-       0x00,       /*  __u16 idProduct; */
-       0x00,
-       0x00,       /*  __u16 bcdDevice; */
-       0x00,
-       0x00,       /*  __u8  iManufacturer; */
-       0x01,       /*  __u8  iProduct; */
-       0x00,       /*  __u8  iSerialNumber; */
-       0x01        /*  __u8  bNumConfigurations; */
-};
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
-       0x09,       /*  __u8  bLength; */
-       0x02,       /*  __u8  bDescriptorType; Configuration */
-       0x19,       /*  __u16 wTotalLength; */
-       0x00,
-       0x01,       /*  __u8  bNumInterfaces; */
-       0x01,       /*  __u8  bConfigurationValue; */
-       0x00,       /*  __u8  iConfiguration; */
-       0x40,       /*  __u8  bmAttributes;
-        Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
-       0x00,       /*  __u8  MaxPower; */
-
-       /* interface */
-       0x09,       /*  __u8  if_bLength; */
-       0x04,       /*  __u8  if_bDescriptorType; Interface */
-       0x00,       /*  __u8  if_bInterfaceNumber; */
-       0x00,       /*  __u8  if_bAlternateSetting; */
-       0x01,       /*  __u8  if_bNumEndpoints; */
-       0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
-       0x00,       /*  __u8  if_bInterfaceSubClass; */
-       0x00,       /*  __u8  if_bInterfaceProtocol; */
-       0x00,       /*  __u8  if_iInterface; */
-
-       /* endpoint */
-       0x07,       /*  __u8  ep_bLength; */
-       0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
-       0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
-       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
-       0x02,       /*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
-       0x00,
-       0xff        /*  __u8  ep_bInterval; 255 ms */
-};
-
-static unsigned char root_hub_str_index0[] =
-{
-       0x04,                   /*  __u8  bLength; */
-       0x03,                   /*  __u8  bDescriptorType; String-descriptor */
-       0x09,                   /*  __u8  lang ID */
-       0x04,                   /*  __u8  lang ID */
-};
-
-static unsigned char root_hub_str_index1[] =
-{
-       28,                     /*  __u8  bLength; */
-       0x03,                   /*  __u8  bDescriptorType; String-descriptor */
-       'O',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'H',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'C',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'I',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       ' ',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'R',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'o',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'o',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       't',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       ' ',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'H',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'u',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-       'b',                    /*  __u8  Unicode */
-       0,                              /*  __u8  Unicode */
-};
+#include <usbroothubdes.h>
 
 /* Hub class-specific descriptor is constructed dynamically */
 
@@ -1199,16 +1207,16 @@ static unsigned char root_hub_str_index1[] =
 #define OK(x)                  len = (x); break
 #ifdef DEBUG
 #define WR_RH_STAT(x)          {info("WR:status %#8x", (x)); ohci_writel((x), \
-                                               &gohci.regs->roothub.status); }
+                                               &ohci->regs->roothub.status); }
 #define WR_RH_PORTSTAT(x)      {info("WR:portstatus[%d] %#8x", wIndex-1, \
-       (x)); ohci_writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); }
+       (x)); ohci_writel((x), &ohci->regs->roothub.portstatus[wIndex-1]); }
 #else
-#define WR_RH_STAT(x)          ohci_writel((x), &gohci.regs->roothub.status)
+#define WR_RH_STAT(x)          ohci_writel((x), &ohci->regs->roothub.status)
 #define WR_RH_PORTSTAT(x)      ohci_writel((x), \
-                                   &gohci.regs->roothub.portstatus[wIndex-1])
+                                   &ohci->regs->roothub.portstatus[wIndex-1])
 #endif
-#define RD_RH_STAT             roothub_status(&gohci)
-#define RD_RH_PORTSTAT         roothub_portstatus(&gohci, wIndex-1)
+#define RD_RH_STAT             roothub_status(ohci)
+#define RD_RH_PORTSTAT         roothub_portstatus(ohci, wIndex-1)
 
 /* request to virtual root hub */
 
@@ -1236,8 +1244,9 @@ int rh_check_port_status(ohci_t *controller)
        return res;
 }
 
-static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
-               void *buffer, int transfer_len, struct devrequest *cmd)
+static int ohci_submit_rh_msg(ohci_t *ohci, struct usb_device *dev,
+       unsigned long pipe, void *buffer, int transfer_len,
+       struct devrequest *cmd)
 {
        void *data = buffer;
        int leni = transfer_len;
@@ -1250,10 +1259,10 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
        ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32));
 
 #ifdef DEBUG
-pkt_print(NULL, dev, pipe, buffer, transfer_len,
+pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len,
          cmd, "SUB(rh)", usb_pipein(pipe));
 #else
-       mdelay(1);
+       ohci_mdelay(1);
 #endif
        if (usb_pipeint(pipe)) {
                info("Root-Hub submit IRQ: NOT implemented");
@@ -1334,7 +1343,6 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
                        OK(0);
                case (RH_PORT_POWER):
                        WR_RH_PORTSTAT(RH_PS_PPS);
-                       mdelay(100);
                        OK(0);
                case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
                        if (RD_RH_PORTSTAT & RH_PS_CCS)
@@ -1344,7 +1352,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
                break;
 
        case RH_SET_ADDRESS:
-               gohci.rh.devnum = wValue;
+               ohci->rh.devnum = wValue;
                OK(0);
 
        case RH_GET_DESCRIPTOR:
@@ -1389,7 +1397,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
 
        case RH_GET_DESCRIPTOR | RH_CLASS:
        {
-               __u32 temp = roothub_a(&gohci);
+               __u32 temp = roothub_a(ohci);
 
                databuf[0] = 9;         /* min length; */
                databuf[1] = 0x29;
@@ -1408,7 +1416,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
                databuf[4] = 0;
                databuf[5] = (temp & RH_A_POTPGT) >> 24;
                databuf[6] = 0;
-               temp = roothub_b(&gohci);
+               temp = roothub_b(ohci);
                databuf[7] = temp & RH_B_DR;
                if (databuf[2] < 7) {
                        databuf[8] = 0xff;
@@ -1437,9 +1445,9 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
        }
 
 #ifdef DEBUG
-       ohci_dump_roothub(&gohci, 1);
+       ohci_dump_roothub(ohci, 1);
 #else
-       mdelay(1);
+       ohci_mdelay(1);
 #endif
 
        len = min_t(int, len, leni);
@@ -1449,10 +1457,10 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
        dev->status = stat;
 
 #ifdef DEBUG
-       pkt_print(NULL, dev, pipe, buffer,
+       pkt_print(ohci, NULL, dev, pipe, buffer,
                  transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/);
 #else
-       mdelay(1);
+       ohci_mdelay(1);
 #endif
 
        return stat;
@@ -1460,18 +1468,43 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
 
 /*-------------------------------------------------------------------------*/
 
+static ohci_dev_t *ohci_get_ohci_dev(ohci_t *ohci, int devnum, int intr)
+{
+       int i;
+
+       if (!intr)
+               return &ohci->ohci_dev;
+
+       /* First see if we already have an ohci_dev for this dev. */
+       for (i = 0; i < NUM_INT_DEVS; i++) {
+               if (ohci->int_dev[i].devnum == devnum)
+                       return &ohci->int_dev[i];
+       }
+
+       /* If not then find a free one. */
+       for (i = 0; i < NUM_INT_DEVS; i++) {
+               if (ohci->int_dev[i].devnum == -1) {
+                       ohci->int_dev[i].devnum = devnum;
+                       return &ohci->int_dev[i];
+               }
+       }
+
+       printf("ohci: Error out of ohci_devs for interrupt endpoints\n");
+       return NULL;
+}
+
 /* common code for handling submit messages - used for all but root hub */
 /* accesses. */
-int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
-               int transfer_len, struct devrequest *setup, int interval)
+static urb_priv_t *ohci_alloc_urb(struct usb_device *dev, unsigned long pipe,
+               void *buffer, int transfer_len, int interval)
 {
-       int stat = 0;
-       int maxsize = usb_maxpacket(dev, pipe);
-       int timeout;
        urb_priv_t *urb;
 
-       urb = malloc(sizeof(urb_priv_t));
-       memset(urb, 0, sizeof(urb_priv_t));
+       urb = calloc(1, sizeof(urb_priv_t));
+       if (!urb) {
+               printf("ohci: Error out of memory allocating urb\n");
+               return NULL;
+       }
 
        urb->dev = dev;
        urb->pipe = pipe;
@@ -1479,18 +1512,29 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
        urb->transfer_buffer_length = transfer_len;
        urb->interval = interval;
 
-       /* device pulled? Shortcut the action. */
-       if (devgone == dev) {
-               dev->status = USB_ST_CRC_ERR;
-               return 0;
-       }
+       return urb;
+}
+
+static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
+               unsigned long pipe, void *buffer, int transfer_len,
+               struct devrequest *setup, int interval)
+{
+       int stat = 0;
+       int maxsize = usb_maxpacket(dev, pipe);
+       int timeout;
+       urb_priv_t *urb;
+       ohci_dev_t *ohci_dev;
+
+       urb = ohci_alloc_urb(dev, pipe, buffer, transfer_len, interval);
+       if (!urb)
+               return -ENOMEM;
 
 #ifdef DEBUG
        urb->actual_length = 0;
-       pkt_print(urb, dev, pipe, buffer, transfer_len,
+       pkt_print(ohci, urb, dev, pipe, buffer, transfer_len,
                  setup, "SUB", usb_pipein(pipe));
 #else
-       mdelay(1);
+       ohci_mdelay(1);
 #endif
        if (!maxsize) {
                err("submit_common_message: pipesize for pipe %lx is zero",
@@ -1498,14 +1542,18 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                return -1;
        }
 
-       if (sohci_submit_job(urb, setup) < 0) {
+       ohci_dev = ohci_get_ohci_dev(ohci, dev->devnum, usb_pipeint(pipe));
+       if (!ohci_dev)
+               return -ENOMEM;
+
+       if (sohci_submit_job(ohci, ohci_dev, urb, setup) < 0) {
                err("sohci_submit_job failed");
                return -1;
        }
 
 #if 0
        mdelay(10);
-       /* ohci_dump_status(&gohci); */
+       /* ohci_dump_status(ohci); */
 #endif
 
        timeout = USB_TIMEOUT_MS(pipe);
@@ -1513,7 +1561,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
        /* wait for it to complete */
        for (;;) {
                /* check whether the controller is done */
-               stat = hc_interrupt();
+               stat = hc_interrupt(ohci);
                if (stat < 0) {
                        stat = USB_ST_CRC_ERR;
                        break;
@@ -1539,7 +1587,8 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                                dbg("*");
 
                } else {
-                       err("CTL:TIMEOUT ");
+                       if (!usb_pipeint(pipe))
+                               err("CTL:TIMEOUT ");
                        dbg("submit_common_msg: TO status %x\n", stat);
                        urb->finished = 1;
                        stat = USB_ST_CRC_ERR;
@@ -1548,62 +1597,170 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
        }
 
        dev->status = stat;
-       dev->act_len = transfer_len;
+       dev->act_len = urb->actual_length;
+
+       if (usb_pipein(pipe) && dev->status == 0 && dev->act_len)
+               invalidate_dcache_buffer(buffer, dev->act_len);
 
 #ifdef DEBUG
-       pkt_print(urb, dev, pipe, buffer, transfer_len,
+       pkt_print(ohci, urb, dev, pipe, buffer, transfer_len,
                  setup, "RET(ctlr)", usb_pipein(pipe));
 #else
-       mdelay(1);
+       ohci_mdelay(1);
 #endif
+       urb_free_priv(urb);
+       return 0;
+}
+
+#define MAX_INT_QUEUESIZE 8
+
+struct int_queue {
+       int queuesize;
+       int curr_urb;
+       urb_priv_t *urb[MAX_INT_QUEUESIZE];
+};
+
+static struct int_queue *_ohci_create_int_queue(ohci_t *ohci,
+               struct usb_device *udev, unsigned long pipe, int queuesize,
+               int elementsize, void *buffer, int interval)
+{
+       struct int_queue *queue;
+       ohci_dev_t *ohci_dev;
+       int i;
+
+       if (queuesize > MAX_INT_QUEUESIZE)
+               return NULL;
+
+       ohci_dev = ohci_get_ohci_dev(ohci, udev->devnum, 1);
+       if (!ohci_dev)
+               return NULL;
+
+       queue = malloc(sizeof(*queue));
+       if (!queue) {
+               printf("ohci: Error out of memory allocating int queue\n");
+               return NULL;
+       }
+
+       for (i = 0; i < queuesize; i++) {
+               queue->urb[i] = ohci_alloc_urb(udev, pipe,
+                                              buffer + i * elementsize,
+                                              elementsize, interval);
+               if (!queue->urb[i])
+                       break;
+
+               if (sohci_submit_job(ohci, ohci_dev, queue->urb[i], NULL)) {
+                       printf("ohci: Error submitting int queue job\n");
+                       urb_free_priv(queue->urb[i]);
+                       break;
+               }
+       }
+       if (i == 0) {
+               /* We did not succeed in submitting even 1 urb */
+               free(queue);
+               return NULL;
+       }
+
+       queue->queuesize = i;
+       queue->curr_urb = 0;
+
+       return queue;
+}
+
+static void *_ohci_poll_int_queue(ohci_t *ohci, struct usb_device *udev,
+                                 struct int_queue *queue)
+{
+       if (queue->curr_urb == queue->queuesize)
+               return NULL; /* Queue depleted */
+
+       if (hc_interrupt(ohci) < 0)
+               return NULL;
+
+       if (queue->urb[queue->curr_urb]->finished) {
+               void *ret = queue->urb[queue->curr_urb]->transfer_buffer;
+               queue->curr_urb++;
+               return ret;
+       }
+
+       return NULL;
+}
+
+static int _ohci_destroy_int_queue(ohci_t *ohci, struct usb_device *dev,
+                                  struct int_queue *queue)
+{
+       int i;
+
+       for (i = 0; i < queue->queuesize; i++)
+               urb_free_priv(queue->urb[i]);
+
+       free(queue);
 
-       /* free TDs in urb_priv */
-       if (!usb_pipeint(pipe))
-               urb_free_priv(urb);
        return 0;
 }
 
+#ifndef CONFIG_DM_USB
 /* submit routines called from usb.c */
 int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                int transfer_len)
 {
        info("submit_bulk_msg");
-       return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0);
+       return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len,
+                                NULL, 0);
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+               int transfer_len, int interval)
+{
+       info("submit_int_msg");
+       return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, NULL,
+                       interval);
+}
+
+struct int_queue *create_int_queue(struct usb_device *dev,
+               unsigned long pipe, int queuesize, int elementsize,
+               void *buffer, int interval)
+{
+       return _ohci_create_int_queue(&gohci, dev, pipe, queuesize,
+                                     elementsize, buffer, interval);
+}
+
+void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+       return _ohci_poll_int_queue(&gohci, dev, queue);
 }
 
-int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
-               int transfer_len, struct devrequest *setup)
+int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+       return _ohci_destroy_int_queue(&gohci, dev, queue);
+}
+#endif
+
+static int _ohci_submit_control_msg(ohci_t *ohci, struct usb_device *dev,
+       unsigned long pipe, void *buffer, int transfer_len,
+       struct devrequest *setup)
 {
        int maxsize = usb_maxpacket(dev, pipe);
 
        info("submit_control_msg");
 #ifdef DEBUG
-       pkt_print(NULL, dev, pipe, buffer, transfer_len,
+       pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len,
                  setup, "SUB", usb_pipein(pipe));
 #else
-       mdelay(1);
+       ohci_mdelay(1);
 #endif
        if (!maxsize) {
                err("submit_control_message: pipesize for pipe %lx is zero",
                        pipe);
                return -1;
        }
-       if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) {
-               gohci.rh.dev = dev;
+       if (((pipe >> 8) & 0x7f) == ohci->rh.devnum) {
+               ohci->rh.dev = dev;
                /* root hub - redirect */
-               return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len,
-                       setup);
+               return ohci_submit_rh_msg(ohci, dev, pipe, buffer,
+                                         transfer_len, setup);
        }
 
-       return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0);
-}
-
-int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
-               int transfer_len, int interval)
-{
-       info("submit_int_msg");
-       return submit_common_msg(dev, pipe, buffer, transfer_len, NULL,
-                       interval);
+       return submit_common_msg(ohci, dev, pipe, buffer, transfer_len,
+                                setup, 0);
 }
 
 /*-------------------------------------------------------------------------*
@@ -1692,8 +1849,11 @@ static int hc_start(ohci_t *ohci)
 {
        __u32 mask;
        unsigned int fminterval;
+       int i;
 
        ohci->disabled = 1;
+       for (i = 0; i < NUM_INT_DEVS; i++)
+               ohci->int_dev[i].devnum = -1;
 
        /* Tell the controller where the control and bulk lists are
         * The lists are empty now. */
@@ -1734,9 +1894,6 @@ static int hc_start(ohci_t *ohci)
        ohci_writel(RH_HS_LPSC, &ohci->regs->roothub.status);
 #endif /* OHCI_USE_NPS */
 
-       /* POTPGT delay is bits 24-31, in 2 ms units. */
-       mdelay((roothub_a(ohci) >> 23) & 0x1fe);
-
        /* connect the virtual root hub */
        ohci->rh.devnum = 0;
 
@@ -1747,13 +1904,14 @@ static int hc_start(ohci_t *ohci)
 
 /* an interrupt happens */
 
-static int hc_interrupt(void)
+static int hc_interrupt(ohci_t *ohci)
 {
-       ohci_t *ohci = &gohci;
        struct ohci_regs *regs = ohci->regs;
        int ints;
        int stat = -1;
 
+       invalidate_dcache_hcca(ohci->hcca);
+
        if ((ohci->hcca->done_head != 0) &&
                                !(m32_swap(ohci->hcca->done_head) & 0x01)) {
                ints =  OHCI_INTR_WDH;
@@ -1787,7 +1945,7 @@ static int hc_interrupt(void)
 #ifdef DEBUG
                ohci_dump(ohci, 1);
 #else
-               mdelay(1);
+               ohci_mdelay(1);
 #endif
                /* FIXME: be optimistic, hope that bug won't repeat often. */
                /* Make some non-interrupt context restart the controller. */
@@ -1798,10 +1956,10 @@ static int hc_interrupt(void)
        }
 
        if (ints & OHCI_INTR_WDH) {
-               mdelay(1);
+               ohci_mdelay(1);
                ohci_writel(OHCI_INTR_WDH, &regs->intrdisable);
                (void)ohci_readl(&regs->intrdisable); /* flush */
-               stat = dl_done_list(&gohci);
+               stat = dl_done_list(ohci);
                ohci_writel(OHCI_INTR_WDH, &regs->intrenable);
                (void)ohci_readl(&regs->intrdisable); /* flush */
        }
@@ -1828,6 +1986,8 @@ static int hc_interrupt(void)
 
 /*-------------------------------------------------------------------------*/
 
+#ifndef CONFIG_DM_USB
+
 /*-------------------------------------------------------------------------*/
 
 /* De-allocate all resources.. */
@@ -1847,7 +2007,7 @@ static void hc_release_ohci(ohci_t *ohci)
  */
 static char ohci_inited = 0;
 
-int usb_lowlevel_init(int index, void **controller)
+int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
 {
 #ifdef CONFIG_PCI_OHCI
        pci_dev_t pdev;
@@ -1861,7 +2021,7 @@ int usb_lowlevel_init(int index, void **controller)
 
 #ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
        /*  board dependant init */
-       if (usb_board_init())
+       if (board_usb_init(index, USB_INIT_HOST))
                return -1;
 #endif
        memset(&gohci, 0, sizeof(ohci_t));
@@ -1871,21 +2031,9 @@ int usb_lowlevel_init(int index, void **controller)
                err("HCCA not aligned!!");
                return -1;
        }
-       phcca = &ghcca[0];
-       info("aligned ghcca %p", phcca);
-       memset(&ohci_dev, 0, sizeof(struct ohci_device));
-       if ((__u32)&ohci_dev.ed[0] & 0x7) {
-               err("EDs not aligned!!");
-               return -1;
-       }
-       memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1));
-       if ((__u32)gtd & 0x7) {
-               err("TDs not aligned!!");
-               return -1;
-       }
-       ptd = gtd;
-       gohci.hcca = phcca;
-       memset(phcca, 0, sizeof(struct ohci_hcca));
+       gohci.hcca = &ghcca[0];
+       info("aligned ghcca %p", gohci.hcca);
+       memset(gohci.hcca, 0, sizeof(struct ohci_hcca));
 
        gohci.disabled = 1;
        gohci.sleeping = 0;
@@ -1918,7 +2066,7 @@ int usb_lowlevel_init(int index, void **controller)
                err ("can't reset usb-%s", gohci.slot_name);
 #ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT
                /* board dependant cleanup */
-               usb_board_init_fail();
+               board_usb_cleanup(index, USB_INIT_HOST);
 #endif
 
 #ifdef CONFIG_SYS_USB_OHCI_CPU_INIT
@@ -1947,7 +2095,7 @@ int usb_lowlevel_init(int index, void **controller)
 #ifdef DEBUG
        ohci_dump(&gohci, 1);
 #else
-       mdelay(1);
+       ohci_mdelay(1);
 #endif
        ohci_inited = 1;
        return 0;
@@ -1979,3 +2127,115 @@ int usb_lowlevel_stop(int index)
        ohci_inited = 0;
        return 0;
 }
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe,
+       void *buffer, int transfer_len, struct devrequest *setup)
+{
+       return _ohci_submit_control_msg(&gohci, dev, pipe, buffer,
+                                       transfer_len, setup);
+}
+#endif
+
+#ifdef CONFIG_DM_USB
+static int ohci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
+                                  unsigned long pipe, void *buffer, int length,
+                                  struct devrequest *setup)
+{
+       ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+       return _ohci_submit_control_msg(ohci, udev, pipe, buffer,
+                                       length, setup);
+}
+
+static int ohci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
+                               unsigned long pipe, void *buffer, int length)
+{
+       ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+       return submit_common_msg(ohci, udev, pipe, buffer, length, NULL, 0);
+}
+
+static int ohci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
+                              unsigned long pipe, void *buffer, int length,
+                              int interval)
+{
+       ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+       return submit_common_msg(ohci, udev, pipe, buffer, length,
+                                NULL, interval);
+}
+
+static struct int_queue *ohci_create_int_queue(struct udevice *dev,
+               struct usb_device *udev, unsigned long pipe, int queuesize,
+               int elementsize, void *buffer, int interval)
+{
+       ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+       return _ohci_create_int_queue(ohci, udev, pipe, queuesize, elementsize,
+                                     buffer, interval);
+}
+
+static void *ohci_poll_int_queue(struct udevice *dev, struct usb_device *udev,
+                                struct int_queue *queue)
+{
+       ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+       return _ohci_poll_int_queue(ohci, udev, queue);
+}
+
+static int ohci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
+                                 struct int_queue *queue)
+{
+       ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+       return _ohci_destroy_int_queue(ohci, udev, queue);
+}
+
+int ohci_register(struct udevice *dev, struct ohci_regs *regs)
+{
+       struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+       ohci_t *ohci = dev_get_priv(dev);
+       u32 reg;
+
+       priv->desc_before_addr = true;
+
+       ohci->regs = regs;
+       ohci->hcca = memalign(256, sizeof(struct ohci_hcca));
+       if (!ohci->hcca)
+               return -ENOMEM;
+       memset(ohci->hcca, 0, sizeof(struct ohci_hcca));
+
+       if (hc_reset(ohci) < 0)
+               return -EIO;
+
+       if (hc_start(ohci) < 0)
+               return -EIO;
+
+       reg = ohci_readl(&regs->revision);
+       printf("USB OHCI %x.%x\n", (reg >> 4) & 0xf, reg & 0xf);
+
+       return 0;
+}
+
+int ohci_deregister(struct udevice *dev)
+{
+       ohci_t *ohci = dev_get_priv(dev);
+
+       if (hc_reset(ohci) < 0)
+               return -EIO;
+
+       free(ohci->hcca);
+
+       return 0;
+}
+
+struct dm_usb_ops ohci_usb_ops = {
+       .control = ohci_submit_control_msg,
+       .bulk = ohci_submit_bulk_msg,
+       .interrupt = ohci_submit_int_msg,
+       .create_int_queue = ohci_create_int_queue,
+       .poll_int_queue = ohci_poll_int_queue,
+       .destroy_int_queue = ohci_destroy_int_queue,
+};
+
+#endif