From 5877de916510cc2030eafe3761a835726956c7d3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Sat, 11 Apr 2015 21:52:02 -0600 Subject: usb: dwc2: retry NAK'd interrupt transfers IIUC, interrupt transfers are NAK'd by devices until they wish to trigger an interrupt, and e.g. EHCI controllers retry these in HW until they are ACK'd. However, DWC2 doesn't seem to retry, so we need to do this in SW. In practice, I've seen DWC2_HCINT_FRMOVRUN happen too. I'm not quite sure what this error implies; perhaps it's related to how near the end of a USB frame we're at when the interrupt transfer is initiated? Anyway, retrying this temporary error seems to be necessary too. With all these commits applied, both my USB keyboards (one LS Lenovo and one FS Dell) work correctly when there is no USB hub between the SoC and the keyboard; We still need split transactions to be implemented for hubs to work. Signed-off-by: Stephen Warren --- drivers/usb/host/dwc2.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index f3fbe76acf..2ac00177a2 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -723,6 +723,8 @@ int wait_for_chhltd(uint32_t *sub, int *toggle, bool ignore_ack) return ret; hcint = readl(&hc_regs->hcint); + if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) + return -EAGAIN; if (ignore_ack) hcint &= ~DWC2_HCINT_ACK; else @@ -758,7 +760,7 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, int max = usb_maxpacket(dev, pipe); int eptype = dwc2_eptype[usb_pipetype(pipe)]; int done = 0; - int ret; + int ret = 0; uint32_t sub; uint32_t xfer_len; uint32_t num_packets; @@ -813,10 +815,8 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, DWC2_HCCHAR_CHEN); ret = wait_for_chhltd(&sub, pid, ignore_ack); - if (ret) { - stop_transfer = 1; + if (ret) break; - } if (in) { xfer_len -= sub; @@ -835,7 +835,7 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, dev->status = 0; dev->act_len = done; - return 0; + return ret; } /* U-Boot USB transmission interface */ @@ -904,8 +904,21 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, int interval) { + unsigned long timeout; + int ret; + /* FIXME: what is interval? */ - return submit_bulk_msg(dev, pipe, buffer, len); + + timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); + for (;;) { + if (get_timer(0) > timeout) { + printf("Timeout poll on interrupt endpoint\n"); + return -ETIMEDOUT; + } + ret = submit_bulk_msg(dev, pipe, buffer, len); + if (ret != -EAGAIN) + return ret; + } } /* U-Boot USB control interface */ -- cgit