summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorStefan Brüns <stefan.bruens@rwth-aachen.de>2016-01-17 04:09:53 +0100
committerMarek Vasut <marex@denx.de>2016-01-23 16:21:11 +0100
commitdaed305941eb7ff30039af8142c2392bd5c3ad91 (patch)
tree3a53c7892ec3bd24a781628ab660ea964da71667 /drivers/usb/host
parent03460cdc3d73e06596e5925fa4a19d7e1da68b29 (diff)
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>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/dwc2.c112
1 files changed, 64 insertions, 48 deletions
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index ad097cbda41..0e710d9d8af 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -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);