In contrast to non-SPLIT transfers each transaction has to be submitted as an individual chunk. Handling of ACK/NAk/NYET handshakes depends on transaction (non-SPLIT/SSPLIT/CSPLIT), thus inline the HCINT flag handling.
Signed-off-by: Stefan Brüns <stefan.bru...@rwth-aachen.de> --- drivers/usb/host/dwc2.c | 96 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 6496fcf..0bf3ee5 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -848,8 +848,7 @@ static int dwc2_eptype[] = { }; int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, - unsigned long pipe, int *pid, int in, void *buffer, int len, - bool ignore_ack) + unsigned long pipe, int *pid, int in, void *buffer, int len) { struct dwc2_core_regs *regs = priv->regs; struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; @@ -863,23 +862,50 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, uint32_t xfer_len; uint32_t num_packets; int stop_transfer = 0; + uint32_t hctsiz; + uint32_t hcint; + uint32_t hcint_rem; + uint8_t do_split = 0; + uint8_t complete_split = 0; + uint8_t start_again = 0; + uint8_t hub_addr = 0; + uint8_t hub_port = 0; debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid, in, len); + /* Initialize channel */ + 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) { + uint32_t hprt0 = readl(®s->hprt0); + if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == + DWC2_HPRT0_PRTSPD_HIGH) { + do_split = 1; + dwc_find_hub_address_port(dev, &hub_addr, &hub_port); + } + } + do { - /* Initialize channel */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, - eptype, max); + /* Clear old interrupt conditions for this host channel. */ + writel(0x3fff, &hc_regs->hcint); + + if (do_split) + dwc_otg_hc_init_split(regs, DWC2_HC_CHANNEL, hub_addr, + hub_port, complete_split); xfer_len = len - done; + if (do_split && xfer_len > max) + xfer_len = max; if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1; if (xfer_len > DWC2_DATA_BUF_SIZE) xfer_len = DWC2_DATA_BUF_SIZE - max + 1; /* Make sure that xfer_len is a multiple of max packet size. */ - if (xfer_len > 0) { + if (xfer_len > max) { num_packets = (xfer_len + max - 1) / max; if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) { num_packets = CONFIG_DWC2_MAX_PACKET_COUNT; @@ -918,10 +944,54 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN); - ret = wait_for_chhltd(regs, &sub, pid, ignore_ack); + ret = wait_for_bit(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true); if (ret) break; + hcint = readl(&hc_regs->hcint); + hcint_rem = hcint & ~(DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD); + + hctsiz = readl(&hc_regs->hctsiz); + sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> + DWC2_HCTSIZ_XFERSIZE_OFFSET; + *pid = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> + DWC2_HCTSIZ_PID_OFFSET; + + start_again = 0; + if (complete_split) { + complete_split = 0; + if (hcint_rem & DWC2_HCINT_NYET) { + hcint_rem &= ~DWC2_HCINT_NYET; + start_again = 1; + } + } else if (do_split) { + if (hcint_rem & DWC2_HCINT_NAK) { + hcint_rem &= ~DWC2_HCINT_NAK; + /* should never happen, as there are no + * concurrent transactions */ + printf("TT busy\n"); + ret = -EINVAL; + } else if (hcint_rem & DWC2_HCINT_ACK) { + complete_split = 1; + start_again = 1; + } + } + + if (start_again) { + sub = 0; + xfer_len = 0; + } + + if (hcint_rem & ~(DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN | + DWC2_HCINT_ACK)) { + debug("%s: Error (HCINT=%08x)\n", __func__, hcint); + ret = -EINVAL; + break; + } else if (hcint_rem & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) { + ret = -EAGAIN; + break; + } + if (in) { xfer_len -= sub; @@ -930,13 +1000,13 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, roundup(xfer_len, ARCH_DMA_MINALIGN))); memcpy(buffer + done, priv->aligned_buffer, xfer_len); - if (sub) + if (sub) /* short transfer/ZLP */ stop_transfer = 1; } done += xfer_len; - } while ((done < len) && !stop_transfer); + } while (((done < len) && !stop_transfer) || start_again); writel(0, &hc_regs->hcintmsk); writel(0xFFFFFFFF, &hc_regs->hcint); @@ -960,7 +1030,7 @@ int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev, } return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep], - usb_pipein(pipe), buffer, len, true); + usb_pipein(pipe), buffer, len); } static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, @@ -980,7 +1050,7 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, } pid = DWC2_HC_PID_SETUP; - ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8, true); + ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8); if (ret) return ret; @@ -995,7 +1065,7 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, return -ETIMEDOUT; } ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), - buffer, len, false); + buffer, len); act_len += dev->act_len; buffer += dev->act_len; len -= dev->act_len; @@ -1013,7 +1083,7 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, pid = DWC2_HC_PID_DATA1; do { ret = chunk_msg(priv, dev, pipe, &pid, status_direction, - priv->status_buffer, 0, false); + priv->status_buffer, 0); } while (ret == -EAGAIN); if (ret) return ret; -- 2.1.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot