usb: dwc2: split transfer core from outer loop
authorStefan Brüns <stefan.bruens@rwth-aachen.de>
Sun, 17 Jan 2016 03:09:53 +0000 (04:09 +0100)
committerMarek Vasut <marex@denx.de>
Sat, 23 Jan 2016 15:21:11 +0000 (16:21 +0100)
Split the movement of data between CPU and Host Controller from the
status handling and tracking of transfer progress.
This will also simplify adding of SPLIT transaction support.

Signed-off-by: Stefan Brüns <stefan.bruens@rwth-aachen.de>
drivers/usb/host/dwc2.c

index ad097cbda4123581b37ed6a883eba37fe8eef6ef..0e710d9d8af126af68604f4b0480139f25a0696e 100644 (file)
@@ -426,9 +426,6 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num,
        if (dev->speed == USB_SPEED_LOW)
                hcchar |= DWC2_HCCHAR_LSPDDEV;
 
-       /* Clear old interrupt conditions for this host channel. */
-       writel(0x3fff, &hc_regs->hcint);
-
        /*
         * Program the HCCHARn register with the endpoint characteristics
         * for the current transfer.
@@ -729,9 +726,8 @@ static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev,
        return stat;
 }
 
-int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle)
+int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, int *toggle)
 {
-       struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
        int ret;
        uint32_t hcint, hctsiz;
 
@@ -765,6 +761,58 @@ static int dwc2_eptype[] = {
        DWC2_HCCHAR_EPTYPE_BULK,
 };
 
+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)
+{
+       int ret = 0;
+       uint32_t sub;
+
+       debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
+             *pid, xfer_len, num_packets);
+
+       writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) |
+              (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) |
+              (*pid << DWC2_HCTSIZ_PID_OFFSET),
+              &hc_regs->hctsiz);
+
+       if (!in && xfer_len) {
+               memcpy(aligned_buffer, buffer, xfer_len);
+
+               flush_dcache_range((unsigned long)aligned_buffer,
+                                  (unsigned long)aligned_buffer +
+                                  roundup(xfer_len, ARCH_DMA_MINALIGN));
+       }
+
+       writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma);
+
+       /* Clear old interrupt conditions for this host channel. */
+       writel(0x3fff, &hc_regs->hcint);
+
+       /* 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,
+                       (1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
+                       DWC2_HCCHAR_CHEN);
+
+       ret = wait_for_chhltd(hc_regs, &sub, pid);
+       if (ret < 0)
+               return ret;
+
+       if (in) {
+               xfer_len -= sub;
+
+               invalidate_dcache_range((unsigned long)aligned_buffer,
+                                       (unsigned long)aligned_buffer +
+                                       roundup(xfer_len, ARCH_DMA_MINALIGN));
+
+               memcpy(buffer, aligned_buffer, xfer_len);
+       }
+       *actual_len = xfer_len;
+
+       return ret;
+}
+
 int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
              unsigned long pipe, int *pid, int in, void *buffer, int len)
 {
@@ -776,7 +824,6 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
        int eptype = dwc2_eptype[usb_pipetype(pipe)];
        int done = 0;
        int ret = 0;
-       uint32_t sub;
        uint32_t xfer_len;
        uint32_t num_packets;
        int stop_transfer = 0;
@@ -795,11 +842,12 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
        num_packets = max_xfer_len / max;
        max_xfer_len = num_packets * max;
 
-       do {
-               /* Initialize channel */
-               dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
-                               eptype, max);
+       /* Initialize channel */
+       dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
+                       eptype, max);
 
+       do {
+               int actual_len = 0;
                xfer_len = len - done;
 
                if (xfer_len > max_xfer_len)
@@ -809,49 +857,17 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
                else
                        num_packets = 1;
 
-               debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
-                     *pid, xfer_len, num_packets);
+               ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid,
+                                    in, (char *)buffer + done, num_packets,
+                                    xfer_len, &actual_len);
 
-               writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) |
-                      (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) |
-                      (*pid << DWC2_HCTSIZ_PID_OFFSET),
-                      &hc_regs->hctsiz);
-
-               if (!in && xfer_len) {
-                       memcpy(priv->aligned_buffer, (char *)buffer + done,
-                              xfer_len);
-
-                       flush_dcache_range((unsigned long)priv->aligned_buffer,
-                               (unsigned long)((void *)priv->aligned_buffer +
-                               roundup(xfer_len, ARCH_DMA_MINALIGN)));
-               }
-
-               writel(phys_to_bus((unsigned long)priv->aligned_buffer),
-                      &hc_regs->hcdma);
-
-               /* 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,
-                               (1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
-                               DWC2_HCCHAR_CHEN);
-
-               ret = wait_for_chhltd(regs, &sub, pid);
                if (ret)
                        break;
 
-               if (in) {
-                       xfer_len -= sub;
-
-                       invalidate_dcache_range((unsigned long)priv->aligned_buffer,
-                               (unsigned long)((void *)priv->aligned_buffer +
-                               roundup(xfer_len, ARCH_DMA_MINALIGN)));
-
-                       memcpy(buffer + done, priv->aligned_buffer, xfer_len);
-                       if (sub)
-                               stop_transfer = 1;
-               }
+               if (actual_len < xfer_len)
+                       stop_transfer = 1;
 
-               done += xfer_len;
+               done += actual_len;
 
        } while ((done < len) && !stop_transfer);