MUSB: Do not enable TX and RX DMA at the same time This is a workaround for OMAP3 errata 1.130. This bug causes an occassional DMA controller hang when two DMA channels are simultaneously enabled in RX and TX directions. With this workaround, in the best case, we effectively use DMA in both directions, and in the worst case, we use PIO mode in one direction. The average throughput is better than using PIO mode in one direction all the time. Signed-off-by: Anand Gadiyar <gadiyar@xxxxxx> Signed-off-by: Nilkesh Patra <Nilkesh.patra@xxxxxx> Signed-off-by: Ajay Kumar Gupta <ajay.gupta@xxxxxx> --- (v3. Fix compilation, and test before sending out. Today's just not my day :( ) - To reproduce the hang condition, use g_ether and start pings in both directions - higher ping sizes reproduce the failure faster. - Needs another patch [1] from me to work reliably. [1] <http://marc.info/?l=linux-usb&m=125897074717960&w=2> - An alternative workaround for this bug involves using the SystemDMA to do a non-synchronized DMA transfer to unload the RX FIFO, and use the MUSB DMA for all TX channels. If someone wants to use that patch, I can post it as well. drivers/usb/musb/musbhsdma.c | 32 ++++++++++++++++++++++++++++++++ drivers/usb/musb/musbhsdma.h | 2 ++ 2 files changed, 34 insertions(+) Index: linux-omap-2.6/drivers/usb/musb/musbhsdma.c =================================================================== --- linux-omap-2.6.orig/drivers/usb/musb/musbhsdma.c +++ linux-omap-2.6/drivers/usb/musb/musbhsdma.c @@ -151,6 +151,11 @@ static void configure_channel(struct dma ? (1 << MUSB_HSDMA_TRANSMIT_SHIFT) : 0); + if (musb_channel->transmit) + controller->tx_active |= (1 << bchannel); + else + controller->rx_active |= (1 << bchannel); + /* address/count */ musb_write_hsdma_addr(mbase, bchannel, dma_addr); musb_write_hsdma_count(mbase, bchannel, len); @@ -166,6 +171,8 @@ static int dma_channel_program(struct dm dma_addr_t dma_addr, u32 len) { struct musb_dma_channel *musb_channel = channel->private_data; + struct musb_dma_controller *controller = musb_channel->controller; + struct musb *musb = controller->private_data; DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", musb_channel->epnum, @@ -175,6 +182,19 @@ static int dma_channel_program(struct dm BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || channel->status == MUSB_DMA_STATUS_BUSY); + /* In version 1.4, if two DMA channels are simultaneously + * enabled in opposite directions, there is a chance that + * the DMA controller will hang. However, it is safe to + * have multiple DMA channels enabled in the same direction + * at the same time. + */ + if (musb->hwvers == MUSB_HWVERS_1400) { + if (musb_channel->transmit && controller->rx_active) + return false; + else if (!musb_channel->transmit && controller->tx_active) + return false; + } + channel->actual_len = 0; musb_channel->start_addr = dma_addr; musb_channel->len = len; @@ -229,6 +249,11 @@ static int dma_channel_abort(struct dma_ musb_write_hsdma_addr(mbase, bchannel, 0); musb_write_hsdma_count(mbase, bchannel, 0); channel->status = MUSB_DMA_STATUS_FREE; + + if (musb_channel->transmit) + musb_channel->controller->tx_active &= ~(1 << bchannel); + else + musb_channel->controller->rx_active &= ~(1 << bchannel); } return 0; @@ -297,6 +322,13 @@ static irqreturn_t dma_controller_irq(in channel->status = MUSB_DMA_STATUS_FREE; + if (musb_channel->transmit) + controller->tx_active &= + ~(1 << bchannel); + else + controller->rx_active &= + ~(1 << bchannel); + /* completed */ if ((devctl & MUSB_DEVCTL_HM) && (musb_channel->transmit) Index: linux-omap-2.6/drivers/usb/musb/musbhsdma.h =================================================================== --- linux-omap-2.6.orig/drivers/usb/musb/musbhsdma.h +++ linux-omap-2.6/drivers/usb/musb/musbhsdma.h @@ -146,4 +146,6 @@ struct musb_dma_controller { u8 channel_count; u8 used_channels; u8 irq; + u8 tx_active; + u8 rx_active; }; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html