MUSB RTL version 1.4 has a hardware issue when TX and RX DMA channels are simultaneously enabled which results in DMA lockup. Use system DMA for all RX channels as a workaround of this issue as this will have minimal throughput overhead based on the fact that Rx transfers are done in DMA mode-0 on OMAP34x/35x platforms. Also added stub functions for non OMAP platforms so that there is no compilation failure. Another approach to use PIO mode in opposite direction would increase the cpu loading and thus using system DMA is preferred workaround. Signed-off-by: Anand Gadiyar <gadiyar@xxxxxx> Signed-off-by: Ajay Kumar Gupta <ajay.gupta@xxxxxx> --- Patch created against linus'tree + all musb patches in Greg's queue Changes from v1: - Added wrapper function to make it compilable for BlackFins drivers/usb/musb/Kconfig | 9 +++ drivers/usb/musb/musbhsdma.c | 132 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/musbhsdma.h | 10 +++ 3 files changed, 151 insertions(+), 0 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 07fe490..be5bea6 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -157,6 +157,15 @@ config USB_INVENTRA_DMA help Enable DMA transfers using Mentor's engine. +config MUSB_USE_SYSTEM_DMA_WORKAROUND + bool 'Use System DMA for Mentor DMA workaround' + depends on USB_MUSB_HDRC && USB_INVENTRA_DMA && ARCH_OMAP + default y + help + MUSB RTL version 1.4 (OMAP34x/35x) has a hardware issue when TX and RX + DMA channels are simultaneously enabled. To work around this issue, + you can choose to use System DMA for RX channels. + config USB_TI_CPPI_DMA bool depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 1008044..d2dab57 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -37,6 +37,97 @@ #include "musb_core.h" #include "musbhsdma.h" +#ifdef CONFIG_ARCH_OMAP +#include <plat/dma.h> +static void musb_sdma_channel_release(int ch_num) +{ + omap_stop_dma(ch_num); + omap_free_dma(ch_num); +} +static void musb_sdma_channel_abort(int ch_num) +{ + omap_stop_dma(ch_num); +} +static void musb_sdma_channel_program(struct musb *musb, + struct musb_dma_channel *musb_channel, + dma_addr_t dma_addr, u32 len) +{ + /* System DMA */ + /* RX: set src = FIFO */ + omap_set_dma_transfer_params(musb_channel->sysdma_channel, + OMAP_DMA_DATA_TYPE_S8, + len ? len : 1, 1, /* One frame */ + OMAP_DMA_SYNC_ELEMENT, + OMAP24XX_DMA_NO_DEVICE, + 0); /* Src Sync */ + + omap_set_dma_src_params(musb_channel->sysdma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + MUSB_FIFO_ADDRESS(musb->ctrl_phys_base, + musb_channel->epnum), + 0, 0); + + omap_set_dma_dest_params(musb_channel->sysdma_channel, 0, + OMAP_DMA_AMODE_POST_INC, dma_addr, + 0, 0); + + omap_set_dma_dest_data_pack(musb_channel->sysdma_channel, 1); + omap_set_dma_dest_burst_mode(musb_channel->sysdma_channel, + OMAP_DMA_DATA_BURST_16); + + omap_start_dma(musb_channel->sysdma_channel); +} +static void musb_sysdma_completion(int lch, u16 ch_status, void *data) +{ + u32 addr; + unsigned long flags; + + struct dma_channel *channel; + + struct musb_dma_channel *musb_channel = + (struct musb_dma_channel *) data; + struct musb_dma_controller *controller = musb_channel->controller; + struct musb *musb = controller->private_data; + channel = &musb_channel->channel; + + DBG(2, "lch = 0x%d, ch_status = 0x%x\n", lch, ch_status); + spin_lock_irqsave(&musb->lock, flags); + + addr = (u32) omap_get_dma_dst_pos(musb_channel->sysdma_channel); + if (musb_channel->len == 0) + channel->actual_len = 0; + else + channel->actual_len = addr - musb_channel->start_addr; + + DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n", + channel, musb_channel->start_addr, addr, + channel->actual_len, musb_channel->len, + (channel->actual_len < musb_channel->len) ? + "=> reconfig 0 " : " => complete"); + + channel->status = MUSB_DMA_STATUS_FREE; + musb_dma_completion(musb, musb_channel->epnum, musb_channel->transmit); + + spin_unlock_irqrestore(&musb->lock, flags); + return; +} +static int musb_sdma_channel_request(void *musb_channel, int *ch_num) +{ + return omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "MUSB SysDMA", + musb_sysdma_completion, (void *) musb_channel, ch_num); +} +#else +static int musb_sdma_channel_request(void *musb_channel, int *ch_num) +{ + return 1; +} +static void musb_sdma_channel_program(struct musb *musb, + struct musb_dma_channel *musb_channel, + dma_addr_t dma_addr, u32 len) {} +static void musb_sdma_channel_abort(int ch_num) {} +static void musb_sdma_channel_release(int ch_num) {} +#endif /* CONFIG_ARCH_OMAP */ + static int dma_controller_start(struct dma_controller *c) { /* nothing to do */ @@ -77,6 +168,7 @@ static struct dma_channel *dma_channel_allocate(struct dma_controller *c, struct musb_dma_controller *controller = container_of(c, struct musb_dma_controller, controller); struct musb_dma_channel *musb_channel = NULL; + struct musb *musb = controller->private_data; struct dma_channel *channel = NULL; u8 bit; @@ -95,6 +187,32 @@ static struct dma_channel *dma_channel_allocate(struct dma_controller *c, /* Tx => mode 1; Rx => mode 0 */ channel->desired_mode = transmit; channel->actual_len = 0; + musb_channel->sysdma_channel = -1; + + /* + * MUSB RTL version 1.4 (OMAP34x/35x) has a hardware + * issue when TX and RX DMA channels are simultaneously + * enabled. To work around this issue, use system DMA + * for all RX channels. + */ + if (((musb->hwvers == MUSB_HWVERS_1400) && !transmit) + && use_sdma_workaround()) { + int ret; + ret = musb_sdma_channel_request( + (void *) musb_channel, + &(musb_channel->sysdma_channel)); + + if (ret) { + printk(KERN_ERR "request_dma failed:" + " %d\n", ret); + controller->used_channels &= + ~(1 << bit); + channel->status = + MUSB_DMA_STATUS_UNKNOWN; + musb_channel->sysdma_channel = -1; + channel = NULL; + } + } break; } } @@ -114,6 +232,11 @@ static void dma_channel_release(struct dma_channel *channel) ~(1 << musb_channel->idx); channel->status = MUSB_DMA_STATUS_UNKNOWN; + + if (musb_channel->sysdma_channel != -1) { + musb_sdma_channel_release(musb_channel->sysdma_channel); + musb_channel->sysdma_channel = -1; + } } static void configure_channel(struct dma_channel *channel, @@ -122,12 +245,16 @@ static void configure_channel(struct dma_channel *channel, { struct musb_dma_channel *musb_channel = channel->private_data; struct musb_dma_controller *controller = musb_channel->controller; + struct musb *musb = controller->private_data; void __iomem *mbase = controller->base; u8 bchannel = musb_channel->idx; u16 csr = 0; DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", channel, packet_sz, dma_addr, len, mode); + if (musb_channel->sysdma_channel != -1) { + musb_sdma_channel_program(musb, musb_channel, dma_addr, len); + } else { /* Mentor DMA */ if (mode) { csr |= 1 << MUSB_HSDMA_MODE1_SHIFT; @@ -160,6 +287,7 @@ static void configure_channel(struct dma_channel *channel, musb_writew(mbase, MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_CONTROL), csr); + } } static int dma_channel_program(struct dma_channel *channel, @@ -214,6 +342,10 @@ static int dma_channel_abort(struct dma_channel *channel) csr &= ~MUSB_TXCSR_DMAMODE; musb_writew(mbase, offset, csr); } else { + if (musb_channel->sysdma_channel != -1) + musb_sdma_channel_abort( + musb_channel->sysdma_channel); + offset = MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR); diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h index 613f95a..effc89a 100644 --- a/drivers/usb/musb/musbhsdma.h +++ b/drivers/usb/musb/musbhsdma.h @@ -142,6 +142,15 @@ static inline void musb_write_hsdma_count(void __iomem *mbase, #define MUSB_HSDMA_CHANNELS 8 +#define MUSB_FIFO_ADDRESS(base, epnum) \ + ((unsigned long) (base + MUSB_FIFO_OFFSET(epnum))) + +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_WORKAROUND +#define use_sdma_workaround() 1 +#else +#define use_sdma_workaround() 0 +#endif + struct musb_dma_controller; struct musb_dma_channel { @@ -153,6 +162,7 @@ struct musb_dma_channel { u8 idx; u8 epnum; u8 transmit; + int sysdma_channel; }; struct musb_dma_controller { -- 1.6.2.4 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html