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 | 44 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 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 @@ -366,9 +366,47 @@ 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 + * following line (in read_fifo()) down 11 lines + * (currently from line 352 to 361): + * __raw_writel(csr, creg); + * + * So the __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; --