diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/dwc2.c | 112 |
1 files changed, 64 insertions, 48 deletions
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 @@ 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 = ®s->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); |