Merge branch 'master' of git://git.denx.de/u-boot-usb
[oweals/u-boot.git] / drivers / usb / host / dwc2.c
index 0e710d9d8af126af68604f4b0480139f25a0696e..b2f4bc685af165c535eb0ecc2e73f583a333a862 100644 (file)
@@ -13,6 +13,7 @@
 #include <memalign.h>
 #include <phys2bus.h>
 #include <usbroothubdes.h>
+#include <wait_bit.h>
 #include <asm/io.h>
 
 #include "dwc2.h"
@@ -34,7 +35,8 @@ struct dwc2_priv {
        uint8_t *aligned_buffer;
        uint8_t *status_buffer;
 #endif
-       int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
+       u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
+       u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
        struct dwc2_core_regs *regs;
        int root_hub_devnum;
 };
@@ -52,27 +54,6 @@ static struct dwc2_priv local;
 /*
  * DWC2 IP interface
  */
-static int wait_for_bit(void *reg, const uint32_t mask, bool set)
-{
-       unsigned int timeout = 1000000;
-       uint32_t val;
-
-       while (--timeout) {
-               val = readl(reg);
-               if (!set)
-                       val = ~val;
-
-               if ((val & mask) == mask)
-                       return 0;
-
-               udelay(1);
-       }
-
-       debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n",
-             __func__, reg, mask, set);
-
-       return -ETIMEDOUT;
-}
 
 /*
  * Initializes the FSLSPClkSel field of the HCFG register
@@ -117,7 +98,8 @@ static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num)
 
        writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET),
               &regs->grstctl);
-       ret = wait_for_bit(&regs->grstctl, DWC2_GRSTCTL_TXFFLSH, 0);
+       ret = wait_for_bit(__func__, &regs->grstctl, DWC2_GRSTCTL_TXFFLSH,
+                          false, 1000, false);
        if (ret)
                printf("%s: Timeout!\n", __func__);
 
@@ -135,7 +117,8 @@ static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs)
        int ret;
 
        writel(DWC2_GRSTCTL_RXFFLSH, &regs->grstctl);
-       ret = wait_for_bit(&regs->grstctl, DWC2_GRSTCTL_RXFFLSH, 0);
+       ret = wait_for_bit(__func__, &regs->grstctl, DWC2_GRSTCTL_RXFFLSH,
+                          false, 1000, false);
        if (ret)
                printf("%s: Timeout!\n", __func__);
 
@@ -152,13 +135,15 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs)
        int ret;
 
        /* Wait for AHB master IDLE state. */
-       ret = wait_for_bit(&regs->grstctl, DWC2_GRSTCTL_AHBIDLE, 1);
+       ret = wait_for_bit(__func__, &regs->grstctl, DWC2_GRSTCTL_AHBIDLE,
+                          true, 1000, false);
        if (ret)
                printf("%s: Timeout!\n", __func__);
 
        /* Core Soft Reset */
        writel(DWC2_GRSTCTL_CSFTRST, &regs->grstctl);
-       ret = wait_for_bit(&regs->grstctl, DWC2_GRSTCTL_CSFTRST, 0);
+       ret = wait_for_bit(__func__, &regs->grstctl, DWC2_GRSTCTL_CSFTRST,
+                          false, 1000, false);
        if (ret)
                printf("%s: Timeout!\n", __func__);
 
@@ -243,8 +228,8 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs)
                clrsetbits_le32(&regs->hc_regs[i].hcchar,
                                DWC2_HCCHAR_EPDIR,
                                DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS);
-               ret = wait_for_bit(&regs->hc_regs[i].hcchar,
-                                  DWC2_HCCHAR_CHEN, 0);
+               ret = wait_for_bit(__func__, &regs->hc_regs[i].hcchar,
+                                  DWC2_HCCHAR_CHEN, false, 1000, false);
                if (ret)
                        printf("%s: Timeout!\n", __func__);
        }
@@ -278,7 +263,9 @@ static void dwc_otg_core_init(struct dwc2_core_regs *regs)
 
        /* Program the ULPI External VBUS bit if needed */
 #ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS
-       usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV;
+       usbcfg |= (DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV |
+                  DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR |
+                  DWC2_GUSBCFG_INDICATOR_PASSTHROUGH);
 #else
        usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV;
 #endif
@@ -432,10 +419,23 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num,
         */
        writel(hcchar, &hc_regs->hcchar);
 
-       /* Program the HCSPLIT register for SPLITs */
+       /* Program the HCSPLIT register, default to no SPLIT */
        writel(0, &hc_regs->hcsplt);
 }
 
+static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs,
+                                 uint8_t hub_devnum, uint8_t hub_port)
+{
+       uint32_t hcsplt = 0;
+
+       hcsplt = DWC2_HCSPLT_SPLTENA;
+       hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET;
+       hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET;
+
+       /* Program the HCSPLIT register for SPLITs */
+       writel(hcsplt, &hc_regs->hcsplt);
+}
+
 /*
  * DWC2 to USB API interface
  */
@@ -726,12 +726,13 @@ static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev,
        return stat;
 }
 
-int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, int *toggle)
+int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle)
 {
        int ret;
        uint32_t hcint, hctsiz;
 
-       ret = wait_for_bit(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true);
+       ret = wait_for_bit(__func__, &hc_regs->hcint, DWC2_HCINT_CHHLTD, true,
+                          1000, false);
        if (ret)
                return ret;
 
@@ -762,8 +763,8 @@ static int dwc2_eptype[] = {
 };
 
 static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
-                         int *pid, int in, void *buffer, int num_packets,
-                         int xfer_len, int *actual_len)
+                         u8 *pid, int in, void *buffer, int num_packets,
+                         int xfer_len, int *actual_len, int odd_frame)
 {
        int ret = 0;
        uint32_t sub;
@@ -791,8 +792,10 @@ static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
 
        /* Set host channel enable after all other setup is complete. */
        clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK |
-                       DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS,
+                       DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS |
+                       DWC2_HCCHAR_ODDFRM,
                        (1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
+                       (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) |
                        DWC2_HCCHAR_CHEN);
 
        ret = wait_for_chhltd(hc_regs, &sub, pid);
@@ -814,20 +817,24 @@ static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
 }
 
 int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
-             unsigned long pipe, int *pid, int in, void *buffer, int len)
+             unsigned long pipe, u8 *pid, int in, void *buffer, int len)
 {
        struct dwc2_core_regs *regs = priv->regs;
        struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
+       struct dwc2_host_regs *host_regs = &regs->host_regs;
        int devnum = usb_pipedevice(pipe);
        int ep = usb_pipeendpoint(pipe);
        int max = usb_maxpacket(dev, pipe);
        int eptype = dwc2_eptype[usb_pipetype(pipe)];
        int done = 0;
        int ret = 0;
+       int do_split = 0;
+       int complete_split = 0;
        uint32_t xfer_len;
        uint32_t num_packets;
        int stop_transfer = 0;
        uint32_t max_xfer_len;
+       int ssplit_frame_num = 0;
 
        debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid,
              in, len);
@@ -846,8 +853,27 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
        dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
                        eptype, max);
 
+       /* Check if the target is a FS/LS device behind a HS hub */
+       if (dev->speed != USB_SPEED_HIGH) {
+               uint8_t hub_addr;
+               uint8_t hub_port;
+               uint32_t hprt0 = readl(&regs->hprt0);
+               if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) ==
+                    DWC2_HPRT0_PRTSPD_HIGH) {
+                       usb_find_usb2_hub_address_port(dev, &hub_addr,
+                                                      &hub_port);
+                       dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port);
+
+                       do_split = 1;
+                       num_packets = 1;
+                       max_xfer_len = max;
+               }
+       }
+
        do {
                int actual_len = 0;
+               uint32_t hcint;
+               int odd_frame = 0;
                xfer_len = len - done;
 
                if (xfer_len > max_xfer_len)
@@ -857,9 +883,41 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
                else
                        num_packets = 1;
 
+               if (complete_split)
+                       setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT);
+               else if (do_split)
+                       clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT);
+
+               if (eptype == DWC2_HCCHAR_EPTYPE_INTR) {
+                       int uframe_num = readl(&host_regs->hfnum);
+                       if (!(uframe_num & 0x1))
+                               odd_frame = 1;
+               }
+
                ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid,
                                     in, (char *)buffer + done, num_packets,
-                                    xfer_len, &actual_len);
+                                    xfer_len, &actual_len, odd_frame);
+
+               hcint = readl(&hc_regs->hcint);
+               if (complete_split) {
+                       stop_transfer = 0;
+                       if (hcint & DWC2_HCINT_NYET) {
+                               ret = 0;
+                               int frame_num = DWC2_HFNUM_MAX_FRNUM &
+                                               readl(&host_regs->hfnum);
+                               if (((frame_num - ssplit_frame_num) &
+                                   DWC2_HFNUM_MAX_FRNUM) > 4)
+                                       ret = -EAGAIN;
+                       } else
+                               complete_split = 0;
+               } else if (do_split) {
+                       if (hcint & DWC2_HCINT_ACK) {
+                               ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM &
+                                                  readl(&host_regs->hfnum);
+                               ret = 0;
+                               complete_split = 1;
+                       }
+               }
 
                if (ret)
                        break;
@@ -869,7 +927,11 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 
                done += actual_len;
 
-       } while ((done < len) && !stop_transfer);
+       /* Transactions are done when when either all data is transferred or
+        * there is a short transfer. In case of a SPLIT make sure the CSPLIT
+        * is executed.
+        */
+       } while (((done < len) && !stop_transfer) || complete_split);
 
        writel(0, &hc_regs->hcintmsk);
        writel(0xFFFFFFFF, &hc_regs->hcint);
@@ -886,14 +948,19 @@ int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 {
        int devnum = usb_pipedevice(pipe);
        int ep = usb_pipeendpoint(pipe);
+       u8* pid;
 
-       if (devnum == priv->root_hub_devnum) {
+       if ((devnum >= MAX_DEVICE) || (devnum == priv->root_hub_devnum)) {
                dev->status = 0;
                return -EINVAL;
        }
 
-       return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep],
-                        usb_pipein(pipe), buffer, len);
+       if (usb_pipein(pipe))
+               pid = &priv->in_data_toggle[devnum][ep];
+       else
+               pid = &priv->out_data_toggle[devnum][ep];
+
+       return chunk_msg(priv, dev, pipe, pid, usb_pipein(pipe), buffer, len);
 }
 
 static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
@@ -901,7 +968,8 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
                               struct devrequest *setup)
 {
        int devnum = usb_pipedevice(pipe);
-       int pid, ret, act_len;
+       int ret, act_len;
+       u8 pid;
        /* For CONTROL endpoint pid should start with DATA1 */
        int status_direction;
 
@@ -912,31 +980,39 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
                                             setup);
        }
 
+       /* SETUP stage */
        pid = DWC2_HC_PID_SETUP;
-       ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8);
+       do {
+               ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8);
+       } while (ret == -EAGAIN);
        if (ret)
                return ret;
 
+       /* DATA stage */
+       act_len = 0;
        if (buffer) {
                pid = DWC2_HC_PID_DATA1;
-               ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), buffer,
-                               len);
+               do {
+                       ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe),
+                                       buffer, len);
+                       act_len += dev->act_len;
+                       buffer += dev->act_len;
+                       len -= dev->act_len;
+               } while (ret == -EAGAIN);
                if (ret)
                        return ret;
-               act_len = dev->act_len;
-       } /* End of DATA stage */
-       else
-               act_len = 0;
-
-       /* STATUS stage */
-       if ((len == 0) || usb_pipeout(pipe))
+               status_direction = usb_pipeout(pipe);
+       } else {
+               /* No-data CONTROL always ends with an IN transaction */
                status_direction = 1;
-       else
-               status_direction = 0;
+       }
 
+       /* STATUS stage */
        pid = DWC2_HC_PID_DATA1;
-       ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
-                       priv->status_buffer, 0);
+       do {
+               ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
+                               priv->status_buffer, 0);
+       } while (ret == -EAGAIN);
        if (ret)
                return ret;
 
@@ -993,8 +1069,10 @@ static int dwc2_init_common(struct dwc2_priv *priv)
                     DWC2_HPRT0_PRTRST);
 
        for (i = 0; i < MAX_DEVICE; i++) {
-               for (j = 0; j < MAX_ENDPOINT; j++)
-                       priv->bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0;
+               for (j = 0; j < MAX_ENDPOINT; j++) {
+                       priv->in_data_toggle[i][j] = DWC2_HC_PID_DATA0;
+                       priv->out_data_toggle[i][j] = DWC2_HC_PID_DATA0;
+               }
        }
 
        return 0;