usb: dwc2: split transfer core from outer loop
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>
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index ad097cb..0e710d9 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -426,9 +426,6 @@
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 @@
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 = ®s->hc_regs[DWC2_HC_CHANNEL];
int ret;
uint32_t hcint, hctsiz;
@@ -765,6 +761,58 @@
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 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 @@
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 @@
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);