]> 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 a56aac39cad722abec7648704d53c664843047a6..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>
@@ -103,21 +105,13 @@ static struct pci_device_id ehci_pci_ids[] = {
 # define m32_swap(x) cpu_to_le32(x)
 #endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */
 
-#ifdef CONFIG_DM_USB
-/*
- * We really should do proper cache flushing everywhere, but for now we only
- * do it for new (driver-model) usb code to avoid regressions.
- */
+/* 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))
-#else
-#define flush_dcache_buffer(addr, size)
-#define invalidate_dcache_buffer(addr, size)
-#endif
 
 /* Do not use sizeof(ed / td) as our ed / td structs contain extra members */
 #define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16)
@@ -140,10 +134,12 @@ static struct pci_device_id ehci_pci_ids[] = {
 #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];
+#endif
 
 /* mapping of the OHCI CC status to error codes */
 static int cc_to_error[16] = {
@@ -759,12 +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;
-#ifdef CONFIG_DM_USB
                                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);
-#endif
                                break;
                        }
                        ed_p = &(((struct ed *)
@@ -1474,19 +1468,43 @@ pkt_print(ohci, 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. */
-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)
+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;
@@ -1494,6 +1512,23 @@ static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
        urb->transfer_buffer_length = transfer_len;
        urb->interval = interval;
 
+       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(ohci, urb, dev, pipe, buffer, transfer_len,
@@ -1507,7 +1542,11 @@ static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
                return -1;
        }
 
-       if (sohci_submit_job(ohci, &ohci->ohci_dev, 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;
        }
@@ -1573,6 +1612,92 @@ static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
        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);
+
+       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)
@@ -1590,6 +1715,25 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                        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 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)
@@ -1705,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. */
@@ -1839,6 +1986,8 @@ static int hc_interrupt(ohci_t *ohci)
 
 /*-------------------------------------------------------------------------*/
 
+#ifndef CONFIG_DM_USB
+
 /*-------------------------------------------------------------------------*/
 
 /* De-allocate all resources.. */
@@ -1985,3 +2134,108 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe,
        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