
Per DesignWare USB OTG databook, driver should retry up to 3 times when transaction error (hcint.xacterr) happen. But the 3 times doesn't count when the response is nack (hcint.nak) or frame overrun (hcint.frmoverun)
This patch solved the enumeration error as spotted at socfpga cyclone5_socdk when plugging in certain pendrive.
Signed-off-by: Chin Liang See clsee@altera.com Cc: Marek Vasut marex@denx.de Cc: Dinh Nguyen dinguyen@opensource.altera.com Cc: Dinh Nguyen dinh.linux@gmail.com Cc: Pavel Machek pavel@denx.de Cc: Oleksandr Tymoshenko gonzo@bluezbox.com Cc: Stephen Warren swarren@wwwdotorg.org Cc: Alexander Stein alexanders83@web.de Cc: Peter Griffin peter.griffin@linaro.org --- drivers/usb/host/dwc2.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 541c0f9..f00cd1b 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -786,15 +786,12 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, uint32_t xfer_len; uint32_t num_packets; int stop_transfer = 0; + int error_retry_count = 0;
debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid, in, len);
do { - /* Initialize channel */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, - eptype, max); - xfer_len = len - done; if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1; @@ -818,6 +815,14 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__, *pid, xfer_len, num_packets);
+ /* per DW spec, we can retry up to 3 times */ + error_retry_count = 3; + +error_retry: + /* Initialize channel */ + dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, + eptype, max); + writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | (*pid << DWC2_HCTSIZ_PID_OFFSET), @@ -841,8 +846,14 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, DWC2_HCCHAR_CHEN);
ret = wait_for_chhltd(regs, &sub, pid, ignore_ack); - if (ret) - break; + if (ret) { + if (ret == -EINVAL) + error_retry_count--; + if (error_retry_count) + goto error_retry; + else + break; + }
if (in) { xfer_len -= sub;