From: Anand Gadiyar <gadiyar@xxxxxx> 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> Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx> --- drivers/usb/musb/musbhsdma.c | 32 ++++++++++++++++++++++++++++++++ drivers/usb/musb/musbhsdma.h | 2 ++ 2 files changed, 34 insertions(+), 0 deletions(-) diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index a237550..927abf0 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -151,6 +151,11 @@ static void configure_channel(struct dma_channel *channel, ? (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 dma_channel *channel, 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 dma_channel *channel, 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_channel *channel) 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(int irq, void *private_data) 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) diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h index 1299d92..df87863 100644 --- a/drivers/usb/musb/musbhsdma.h +++ b/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; }; -- 1.6.6.rc0 -- 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