]> git.sur5r.net Git - u-boot/blobdiff - drivers/usb/host/ehci-hcd.c
arm: mx6: usb: kconfig: add USB_EHCI_MX6 kconfig option
[u-boot] / drivers / usb / host / ehci-hcd.c
index 46d01d40e84a773bfb03de537e2ce639563f88bc..bf02221c9f0f5308399acacd1385c60a102ce825 100644 (file)
@@ -875,7 +875,7 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe,
                                      port - 1);
                                reg |= EHCI_PS_PO;
                                ehci_writel(status_reg, reg);
-                               break;
+                               return -ENXIO;
                        } else {
                                int ret;
 
@@ -897,11 +897,22 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe,
                                 */
                                ret = handshake(status_reg, EHCI_PS_PR, 0,
                                                2 * 1000);
-                               if (!ret)
-                                       ctrl->portreset |= 1 << port;
-                               else
+                               if (!ret) {
+                                       reg = ehci_readl(status_reg);
+                                       if ((reg & (EHCI_PS_PE | EHCI_PS_CS))
+                                           == EHCI_PS_CS && !ehci_is_TDI()) {
+                                               debug("port %d full speed --> companion\n", port - 1);
+                                               reg &= ~EHCI_PS_CLEAR;
+                                               reg |= EHCI_PS_PO;
+                                               ehci_writel(status_reg, reg);
+                                               return -ENXIO;
+                                       } else {
+                                               ctrl->portreset |= 1 << port;
+                                       }
+                               } else {
                                        printf("port(%d) reset error\n",
                                               port - 1);
+                               }
                        }
                        break;
                case USB_PORT_FEAT_TEST:
@@ -1203,6 +1214,7 @@ static int _ehci_submit_control_msg(struct usb_device *dev, unsigned long pipe,
 
 struct int_queue {
        int elementsize;
+       unsigned long pipe;
        struct QH *first;
        struct QH *current;
        struct QH *last;
@@ -1252,13 +1264,13 @@ disable_periodic(struct ehci_ctrl *ctrl)
        return 0;
 }
 
-struct int_queue *
-create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
-                int elementsize, void *buffer, int interval)
+static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
+                       unsigned long pipe, int queuesize, int elementsize,
+                       void *buffer, int interval)
 {
        struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
        struct int_queue *result = NULL;
-       int i;
+       uint32_t i, toggle;
 
        /*
         * Interrupt transfers requiring several transactions are not supported
@@ -1298,6 +1310,7 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
                goto fail1;
        }
        result->elementsize = elementsize;
+       result->pipe = pipe;
        result->first = memalign(USB_DMA_MINALIGN,
                                 sizeof(struct QH) * queuesize);
        if (!result->first) {
@@ -1315,6 +1328,8 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
        memset(result->first, 0, sizeof(struct QH) * queuesize);
        memset(result->tds, 0, sizeof(struct qTD) * queuesize);
 
+       toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
        for (i = 0; i < queuesize; i++) {
                struct QH *qh = result->first + i;
                struct qTD *td = result->tds + i;
@@ -1346,7 +1361,9 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
                td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
                debug("communication direction is '%s'\n",
                      usb_pipein(pipe) ? "in" : "out");
-               td->qt_token = cpu_to_hc32((elementsize << 16) |
+               td->qt_token = cpu_to_hc32(
+                       QT_TOKEN_DT(toggle) |
+                       (elementsize << 16) |
                        ((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */
                        0x80); /* active */
                td->qt_buffer[0] =
@@ -1361,6 +1378,7 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
                    cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff);
 
                *buf = buffer + i * elementsize;
+               toggle ^= 1;
        }
 
        flush_dcache_range((unsigned long)buffer,
@@ -1410,10 +1428,13 @@ fail1:
        return NULL;
 }
 
-void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+static void *_ehci_poll_int_queue(struct usb_device *dev,
+                                 struct int_queue *queue)
 {
        struct QH *cur = queue->current;
        struct qTD *cur_td;
+       uint32_t token, toggle;
+       unsigned long pipe = queue->pipe;
 
        /* depleted queue */
        if (cur == NULL) {
@@ -1424,12 +1445,15 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
        cur_td = &queue->tds[queue->current - queue->first];
        invalidate_dcache_range((unsigned long)cur_td,
                                ALIGN_END_ADDR(struct qTD, cur_td, 1));
-       if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) &
-                       QT_TOKEN_STATUS_ACTIVE) {
-               debug("Exit poll_int_queue with no completed intr transfer. token is %x\n",
-                     hc32_to_cpu(cur_td->qt_token));
+       token = hc32_to_cpu(cur_td->qt_token);
+       if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) {
+               debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", token);
                return NULL;
        }
+
+       toggle = QT_TOKEN_GET_DT(token);
+       usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle);
+
        if (!(cur->qh_link & QH_LINK_TERMINATE))
                queue->current++;
        else
@@ -1440,13 +1464,13 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
                                               queue->elementsize));
 
        debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n",
-             hc32_to_cpu(cur_td->qt_token), cur, queue->first);
+             token, cur, queue->first);
        return cur->buffer;
 }
 
 /* Do not free buffers associated with QHs, they're owned by someone else */
-int
-destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+static int _ehci_destroy_int_queue(struct usb_device *dev,
+                                  struct int_queue *queue)
 {
        struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
        int result = -1;
@@ -1503,12 +1527,12 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
        debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
              dev, pipe, buffer, length, interval);
 
-       queue = create_int_queue(dev, pipe, 1, length, buffer, interval);
+       queue = _ehci_create_int_queue(dev, pipe, 1, length, buffer, interval);
        if (!queue)
                return -1;
 
        timeout = get_timer(0) + USB_TIMEOUT_MS(pipe);
-       while ((backbuffer = poll_int_queue(dev, queue)) == NULL)
+       while ((backbuffer = _ehci_poll_int_queue(dev, queue)) == NULL)
                if (get_timer(0) > timeout) {
                        printf("Timeout poll on interrupt endpoint\n");
                        result = -ETIMEDOUT;
@@ -1521,7 +1545,7 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
                return -EINVAL;
        }
 
-       ret = destroy_int_queue(dev, queue);
+       ret = _ehci_destroy_int_queue(dev, queue);
        if (ret < 0)
                return ret;
 
@@ -1547,6 +1571,24 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe,
 {
        return _ehci_submit_int_msg(dev, pipe, buffer, length, interval);
 }
+
+struct int_queue *create_int_queue(struct usb_device *dev,
+               unsigned long pipe, int queuesize, int elementsize,
+               void *buffer, int interval)
+{
+       return _ehci_create_int_queue(dev, pipe, queuesize, elementsize,
+                                     buffer, interval);
+}
+
+void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+       return _ehci_poll_int_queue(dev, queue);
+}
+
+int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+       return _ehci_destroy_int_queue(dev, queue);
+}
 #endif
 
 #ifdef CONFIG_DM_USB
@@ -1575,6 +1617,29 @@ static int ehci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
        return _ehci_submit_int_msg(udev, pipe, buffer, length, interval);
 }
 
+static struct int_queue *ehci_create_int_queue(struct udevice *dev,
+               struct usb_device *udev, unsigned long pipe, int queuesize,
+               int elementsize, void *buffer, int interval)
+{
+       debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+       return _ehci_create_int_queue(udev, pipe, queuesize, elementsize,
+                                     buffer, interval);
+}
+
+static void *ehci_poll_int_queue(struct udevice *dev, struct usb_device *udev,
+                                struct int_queue *queue)
+{
+       debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+       return _ehci_poll_int_queue(udev, queue);
+}
+
+static int ehci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
+                                 struct int_queue *queue)
+{
+       debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+       return _ehci_destroy_int_queue(udev, queue);
+}
+
 int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
                  struct ehci_hcor *hcor, const struct ehci_ops *ops,
                  uint tweaks, enum usb_init_type init)
@@ -1623,6 +1688,9 @@ struct dm_usb_ops ehci_usb_ops = {
        .control = ehci_submit_control_msg,
        .bulk = ehci_submit_bulk_msg,
        .interrupt = ehci_submit_int_msg,
+       .create_int_queue = ehci_create_int_queue,
+       .poll_int_queue = ehci_poll_int_queue,
+       .destroy_int_queue = ehci_destroy_int_queue,
 };
 
 #endif