From: Tony Lindgren Date: Thu, 6 Sep 2007 01:27:56 +0000 (-0700) Subject: musb_hdrc: Fix tusb DMA corruption X-Git-Tag: v2.6.23-omap1~82 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=884b0231a1bfc3b01a06548fca3653c0f7811433;p=linux-2.6-omap-h63xx.git musb_hdrc: Fix tusb DMA corruption It seems tusb has a problem with sync DMA being mixed with async PIO. Wait for the EP XFR_SIZE to clear before re-using the same DMA channel. Signed-off-by: Tony Lindgren --- diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index b6c247cfffd..d33301fad74 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -170,22 +170,24 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) remaining = TUSB_EP_CONFIG_XFR_SIZE(remaining); - /* HW issue #10: XFR_SIZE may get corrupt on async DMA */ + /* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */ if (unlikely(remaining > chdat->transfer_len)) { - WARN("Corrupt XFR_SIZE with DMA: %lu\n", remaining); + DBG(2, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n", + chdat->tx ? "tx" : "rx", chdat->ch, + remaining); remaining = 0; } channel->actual_len = chdat->transfer_len - remaining; pio = chdat->len - channel->actual_len; - DBG(2, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); + DBG(3, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); /* Transfer remaining 1 - 31 bytes */ if (pio > 0 && pio < 32) { u8 *buf; - DBG(2, "Using PIO for remaining %lu bytes\n", pio); + DBG(3, "Using PIO for remaining %lu bytes\n", pio); buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len; if (chdat->tx) { consistent_sync(phys_to_virt((u32)chdat->dma_addr), @@ -244,6 +246,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, void __iomem *ep_conf = hw_ep->conf; dma_addr_t fifo = hw_ep->fifo_sync; struct omap_dma_channel_params dma_params; + u32 dma_remaining; int src_burst, dst_burst; u16 csr; int ch; @@ -262,6 +265,25 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, if (dma_addr & 0x2) return false; + /* + * Because of HW issue #10, it seems like mixing sync DMA and async + * PIO access can confuse the DMA. Make sure XFR_SIZE is reset before + * using the channel for DMA. + */ + if (chdat->tx) + dma_remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET); + else + dma_remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET); + + dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining); + if (dma_remaining) { + DBG(2, "Busy %s dma ch%i, not using: %08x\n", + chdat->tx ? "tx" : "rx", chdat->ch, + dma_remaining); + return false; + } + + chdat->transfer_len = len & ~0x1f; if (len < packet_sz)