mpc8641hpcn: Use physical address in flash banks defintion
[oweals/u-boot.git] / drivers / usb / usb_ehci_core.c
index 07e9a6b8a08827c5671ac07c9a14cc0796c69823..813f64abd30c381645b6dd8b7f25b397ac1f4f57 100644 (file)
@@ -28,7 +28,7 @@
 #include "usb_ehci.h"
 
 int rootdev;
-struct ehci_hccr *hccr;                /* R/O registers, not need for volatile */
+struct ehci_hccr *hccr;        /* R/O registers, not need for volatile */
 volatile struct ehci_hcor *hcor;
 
 static uint16_t portreset;
@@ -106,20 +106,112 @@ static struct descriptor {
 #define ehci_is_TDI()  (0)
 #endif
 
-static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec)
+#if defined(CONFIG_EHCI_DCACHE)
+/*
+ * Routines to handle (flush/invalidate) the dcache for the QH and qTD
+ * structures and data buffers. This is needed on platforms using this
+ * EHCI support with dcache enabled.
+ */
+static void flush_invalidate(u32 addr, int size, int flush)
+{
+       if (flush)
+               flush_dcache_range(addr, addr + size);
+       else
+               invalidate_dcache_range(addr, addr + size);
+}
+
+static void cache_qtd(struct qTD *qtd, int flush)
+{
+       u32 *ptr = (u32 *)qtd->qt_buffer[0];
+       int len = (qtd->qt_token & 0x7fff0000) >> 16;
+
+       flush_invalidate((u32)qtd, sizeof(struct qTD), flush);
+       if (ptr && len)
+               flush_invalidate((u32)ptr, len, flush);
+}
+
+
+static inline struct QH *qh_addr(struct QH *qh)
+{
+       return (struct QH *)((u32)qh & 0xffffffe0);
+}
+
+static void cache_qh(struct QH *qh, int flush)
+{
+       struct qTD *qtd;
+       struct qTD *next;
+       static struct qTD *first_qtd;
+
+       /*
+        * Walk the QH list and flush/invalidate all entries
+        */
+       while (1) {
+               flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush);
+               if ((u32)qh & QH_LINK_TYPE_QH)
+                       break;
+               qh = qh_addr(qh);
+               qh = (struct QH *)qh->qh_link;
+       }
+       qh = qh_addr(qh);
+
+       /*
+        * Save first qTD pointer, needed for invalidating pass on this QH
+        */
+       if (flush)
+               first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay &
+                                                0xffffffe0);
+       else
+               qtd = first_qtd;
+
+       /*
+        * Walk the qTD list and flush/invalidate all entries
+        */
+       while (1) {
+               if (qtd == NULL)
+                       break;
+               cache_qtd(qtd, flush);
+               next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0);
+               if (next == qtd)
+                       break;
+               qtd = next;
+       }
+}
+
+static inline void ehci_flush_dcache(struct QH *qh)
+{
+       cache_qh(qh, 1);
+}
+
+static inline void ehci_invalidate_dcache(struct QH *qh)
+{
+       cache_qh(qh, 0);
+}
+#else /* CONFIG_EHCI_DCACHE */
+/*
+ *
+ */
+static inline void ehci_flush_dcache(struct QH *qh)
+{
+}
+
+static inline void ehci_invalidate_dcache(struct QH *qh)
+{
+}
+#endif /* CONFIG_EHCI_DCACHE */
+
+static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
 {
        uint32_t result;
        do {
                result = ehci_readl(ptr);
-               debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result);
                if (result == ~(uint32_t)0)
                        return -1;
                result &= mask;
                if (result == done)
                        return 0;
-               wait_ms(1);
-               msec--;
-       } while (msec > 0);
+               udelay(1);
+               usec--;
+       } while (usec > 0);
        return -1;
 }
 
@@ -138,21 +230,21 @@ static int ehci_reset(void)
        cmd = ehci_readl(&hcor->or_usbcmd);
        cmd |= CMD_RESET;
        ehci_writel(&hcor->or_usbcmd, cmd);
-       ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250);
+       ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000);
        if (ret < 0) {
                printf("EHCI fail to reset\n");
                goto out;
        }
 
-#if defined(CONFIG_EHCI_IS_TDI)
-       reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE);
-       tmp = ehci_readl(reg_ptr);
-       tmp |= USBMODE_CM_HC;
+       if (ehci_is_TDI()) {
+               reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE);
+               tmp = ehci_readl(reg_ptr);
+               tmp |= USBMODE_CM_HC;
 #if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
-       tmp |= USBMODE_BE;
-#endif
-       ehci_writel(reg_ptr, tmp);
+               tmp |= USBMODE_BE;
 #endif
+               ehci_writel(reg_ptr, tmp);
+       }
 out:
        return ret;
 }
@@ -224,7 +316,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
        uint32_t endpt, token, usbsts;
        uint32_t c, toggle;
        uint32_t cmd;
-       uint32_t sts;
+       int ret = 0;
 
        debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
              buffer, length, req);
@@ -332,6 +424,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
 
        qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH);
 
+       /* Flush dcache */
+       ehci_flush_dcache(&qh_list);
+
        usbsts = ehci_readl(&hcor->or_usbsts);
        ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));
 
@@ -340,16 +435,19 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
        cmd |= CMD_ASE;
        ehci_writel(&hcor->or_usbcmd, cmd);
 
-       sts = ehci_readl(&hcor->or_usbsts);
-       while ((sts & STD_ASS) == 0) {
-               sts = ehci_readl(&hcor->or_usbsts);
-               udelay(10);
+       ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,
+                       100 * 1000);
+       if (ret < 0) {
+               printf("EHCI fail timeout STD_ASS set\n");
+               goto fail;
        }
 
        /* Wait for TDs to be processed. */
        ts = get_timer(0);
        vtd = td;
        do {
+               /* Invalidate dcache */
+               ehci_invalidate_dcache(&qh_list);
                token = hc32_to_cpu(vtd->qt_token);
                if (!(token & 0x80))
                        break;
@@ -360,10 +458,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
        cmd &= ~CMD_ASE;
        ehci_writel(&hcor->or_usbcmd, cmd);
 
-       sts = ehci_readl(&hcor->or_usbsts);
-       while ((sts & STD_ASS) != 0) {
-               sts = ehci_readl(&hcor->or_usbsts);
-               udelay(10);
+       ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0,
+                       100 * 1000);
+       if (ret < 0) {
+               printf("EHCI fail timeout STD_ASS reset\n");
+               goto fail;
        }
 
        qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
@@ -536,7 +635,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
                        /* force reset to complete */
                        reg = reg & ~(EHCI_PS_PR | EHCI_PS_CLEAR);
                        ehci_writel(status_reg, reg);
-                       ret = handshake(status_reg, EHCI_PS_PR, 0, 2);
+                       ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000);
                        if (!ret)
                                tmpbuf[0] |= USB_PORT_STAT_RESET;
                        else
@@ -545,7 +644,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
                }
                if (reg & EHCI_PS_PP)
                        tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
-               tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
+
+               if (ehci_is_TDI()) {
+                       switch ((reg >> 26) & 3) {
+                       case 0:
+                               break;
+                       case 1:
+                               tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;
+                               break;
+                       case 2:
+                       default:
+                               tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
+                               break;
+                       }
+               } else {
+                       tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
+               }
 
                if (reg & EHCI_PS_CSC)
                        tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION;
@@ -675,6 +789,11 @@ int usb_lowlevel_init(void)
        if (ehci_reset() != 0)
                return -1;
 
+#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
+       if (ehci_hcd_init() != 0)
+               return -1;
+#endif
+
        /* Set head of reclaim list */
        memset(&qh_list, 0, sizeof(qh_list));
        qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);