This solves the read_fifo timing issue for example seen with CDC Ethernet. See the comments below for more info. Signed-off-by: Harro Haan --- drivers/usb/gadget/at91_udc.c | 46 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) Index: linux-2.6.31/drivers/usb/gadget/at91_udc.c =================================================================== --- linux-2.6.31.orig/drivers/usb/gadget/at91_udc.c +++ linux-2.6.31/drivers/usb/gadget/at91_udc.c @@ -349,7 +349,7 @@ rescan: } } else csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0); - __raw_writel(csr, creg); + __raw_writel(csr, creg); /* marker1 */ req->req.actual += count; is_done = (count < ep->ep.maxpacket); @@ -359,6 +359,8 @@ rescan: PACKET("%s %p out/%d%s\n", ep->ep.name, &req->req, count, is_done ? " (done)" : ""); + /* marker2 */ + /* * avoid extra trips through IRQ logic for packets already in * the fifo ... maybe preventing an extra (expensive) OUT-NAK @@ -366,9 +368,45 @@ rescan: if (is_done) done(ep, req, 0); else if (ep->is_pingpong) { - bufferspace -= count; - buf += count; - goto rescan; + /* + * read_fifo timing issue + * The cause is probably mentioned at page 606 of the + * AT91SAM9261 datasheet (6062M-ATARM-23-Mar-09): + * "However, RX_DATA_BLK0, TXPKTRDY, RX_DATA_BK1 require wait + * times of 3 UDPCK clock cycles and 3 peripheral clock cycles + * before accessing DPR." + * + * The issue can for example be seen in combination with the + * CDC Ethernet gadget driver. Use the following command on a + * Linux USB Host to ping the USB Device: + * + * ping 1.2.3.4 -s 88 + * + * Some ping replies take more than 2 seconds (or in + * combination with Windows USB Host CDC ECM drivers result in + * ping timeouts). The above ping packets (including header + * etc.) will result in Ethernet packets of 130 bytes, which + * are 3 USB Full Speed Packets (64+64+2). Somehow the last + * small USB packet is causing the problem, because when using + * the following ping command, everything is OK: + * + * ping 1.2.3.4 -s 140 + * + * NOTE: The if-statement becomes only true when moving the + * __raw_writel call in read_fifo() from marker1 to marker2. + * + * So the new __raw_readl call is already causing enough delay. + */ + csr = __raw_readl(creg); + if (((ep->fifo_bank == 1) && (csr & AT91_UDP_RX_DATA_BK0)) || + ((ep->fifo_bank == 0) && (csr & AT91_UDP_RX_DATA_BK1))) { + WARNING("%s: read_fifo: timing, bank %d, csr 0x%x\n", + ep->ep.name, ep->fifo_bank, csr); + } else { + bufferspace -= count; + buf += count; + goto rescan; + } } return is_done; --