diff options
author | Koen Kooi <koen@openembedded.org> | 2009-01-11 11:25:32 +0100 |
---|---|---|
committer | Koen Kooi <koen@openembedded.org> | 2009-01-11 11:26:07 +0100 |
commit | 59251a6dc6e2a75fd5306ae47f5db68c62b965eb (patch) | |
tree | f9a0cdbc5e825c93331617bbfa7a6a1c4e2b03b1 /packages/linux/linux-omap-2.6.27/musb-rxtx.patch | |
parent | 5cbd9ac4ce10fbfd643b31830ac885553ee58658 (diff) |
linux-omap 2.6.27: add patch for musb RX TX bug
Diffstat (limited to 'packages/linux/linux-omap-2.6.27/musb-rxtx.patch')
-rw-r--r-- | packages/linux/linux-omap-2.6.27/musb-rxtx.patch | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/packages/linux/linux-omap-2.6.27/musb-rxtx.patch b/packages/linux/linux-omap-2.6.27/musb-rxtx.patch new file mode 100644 index 0000000000..79089094af --- /dev/null +++ b/packages/linux/linux-omap-2.6.27/musb-rxtx.patch @@ -0,0 +1,283 @@ +MUSB: Workaround for simultaneous TX and RX usage + +MUSB RTL V1.4 has a hardware issue which results in a DMA controller +hang when TX and RX DMA channels are simultaneously enabled. This +affects at least OMAP2430 and OMAP34XX. + +Since RX transfers are in Mode 0 and anyway result in one DMA interrupt +per packet, we can use System DMA to unload the RX fifos. MUSB DMA can +be used for all TX channels as before. + +Tested with full-duplex TX and RX transfers using g_ether. Runs for 24 +hours without a hang. Without this patch, the hang occurs within minutes. + +This issue was first reported by Jon Hunter on [1] + +[1] http://marc.info/?l=linux-omap&m=119634480534453&w=2 + +Signed-off-by: Anand Gadiyar <gadiyar@ti.com> +--- +Patch based on the linux-omap tree. I believe the tabs-to-spaces issue +has been resolved now, but this is the first patch I'm sending out to the +list after that. + +Suggestions welcome for removing the 2 #ifdefs below. One suggestion was to +use a module parameter to decide whether to use system dma or mentor dma. + +Patch updated with a few ifdefs removed and one compilation break resolved. + + drivers/usb/musb/Kconfig | 8 ++ + drivers/usb/musb/musbhsdma.c | 163 +++++++++++++++++++++++++++++++++++-------- + 2 files changed, 142 insertions(+), 29 deletions(-) + +Index: linux-omap-2.6/drivers/usb/musb/musbhsdma.c +=================================================================== +--- linux-omap-2.6.orig/drivers/usb/musb/musbhsdma.c 2008-07-28 10:54:37.000000000 +0530 ++++ linux-omap-2.6/drivers/usb/musb/musbhsdma.c 2008-08-13 11:58:26.272902373 +0530 +@@ -34,6 +34,7 @@ + #include <linux/interrupt.h> + #include <linux/platform_device.h> + #include "musb_core.h" ++#include <mach/dma.h> + + #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) + #include "omap2430.h" +@@ -64,6 +65,9 @@ + + #define MUSB_HSDMA_CHANNELS 8 + ++#define MUSB_FIFO_ADDRESS(epnum) \ ++ ((unsigned long) (OMAP_HSOTG_BASE + MUSB_FIFO_OFFSET(epnum))) ++ + struct musb_dma_controller; + + struct musb_dma_channel { +@@ -75,6 +79,8 @@ struct musb_dma_channel { + u8 bIndex; + u8 epnum; + u8 transmit; ++ ++ int sysdma_channel; + }; + + struct musb_dma_controller { +@@ -93,6 +99,42 @@ static int dma_controller_start(struct d + return 0; + } + ++#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX ++static void musb_sysdma_completion(int lch, u16 ch_status, void *data) ++{ ++ u32 dwAddress; ++ unsigned long flags; ++ ++ struct dma_channel *pChannel; ++ ++ struct musb_dma_channel *pImplChannel = ++ (struct musb_dma_channel *) data; ++ struct musb_dma_controller *controller = pImplChannel->controller; ++ struct musb *musb = controller->pDmaPrivate; ++ pChannel = &pImplChannel->Channel; ++ ++ DBG(2, "lch = 0x%d, ch_status = 0x%x\n", lch, ch_status); ++ spin_lock_irqsave(&musb->lock, flags); ++ ++ dwAddress = (u32) omap_get_dma_dst_pos(pImplChannel->sysdma_channel); ++ pChannel->actual_len = dwAddress - pImplChannel->dwStartAddress; ++ ++ DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n", ++ pChannel, pImplChannel->dwStartAddress, dwAddress, ++ pChannel->actual_len, pImplChannel->len, ++ (pChannel->actual_len < pImplChannel->len) ? ++ "=> reconfig 0": "=> complete"); ++ ++ pChannel->status = MUSB_DMA_STATUS_FREE; ++ musb_dma_completion(musb, pImplChannel->epnum, pImplChannel->transmit); ++ ++ spin_unlock_irqrestore(&musb->lock, flags); ++ return; ++} ++#else ++#define musb_sysdma_completion NULL ++#endif ++ + static void dma_channel_release(struct dma_channel *pChannel); + + static int dma_controller_stop(struct dma_controller *c) +@@ -144,6 +186,29 @@ static struct dma_channel *dma_channel_a + /* Tx => mode 1; Rx => mode 0 */ + pChannel->desired_mode = transmit; + pChannel->actual_len = 0; ++ pImplChannel->sysdma_channel = -1; ++ ++#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX ++ if (!transmit) { ++ int ret; ++ ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, ++ "MUSB SysDMA", musb_sysdma_completion, ++ (void *) pImplChannel, ++ &(pImplChannel->sysdma_channel)); ++ ++ if (ret) { ++ printk(KERN_ERR "request_dma failed:" ++ " %d\n", ret); ++ controller->bmUsedChannels &= ++ ~(1 << bBit); ++ pChannel->status = ++ MUSB_DMA_STATUS_UNKNOWN; ++ pImplChannel->sysdma_channel = -1; ++ pChannel = NULL; ++ } ++ } ++#endif ++ + break; + } + } +@@ -163,6 +228,12 @@ static void dma_channel_release(struct d + ~(1 << pImplChannel->bIndex); + + pChannel->status = MUSB_DMA_STATUS_UNKNOWN; ++ ++ if (pImplChannel->sysdma_channel != -1) { ++ omap_stop_dma(pImplChannel->sysdma_channel); ++ omap_free_dma(pImplChannel->sysdma_channel); ++ pImplChannel->sysdma_channel = -1; ++ } + } + + static void configure_channel(struct dma_channel *pChannel, +@@ -179,41 +250,69 @@ static void configure_channel(struct dma + DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", + pChannel, packet_sz, dma_addr, len, mode); + +- if (mode) { +- csr |= 1 << MUSB_HSDMA_MODE1_SHIFT; +- BUG_ON(len < packet_sz); ++ if (pImplChannel->sysdma_channel != -1) { ++ /* System DMA */ ++ /* RX: set src = FIFO */ ++ ++ omap_set_dma_transfer_params(pImplChannel->sysdma_channel, ++ OMAP_DMA_DATA_TYPE_S8, ++ len, 1, /* One frame */ ++ OMAP_DMA_SYNC_ELEMENT, ++ OMAP24XX_DMA_NO_DEVICE, ++ 0); /* Src Sync */ ++ ++ omap_set_dma_src_params(pImplChannel->sysdma_channel, 0, ++ OMAP_DMA_AMODE_CONSTANT, ++ MUSB_FIFO_ADDRESS(pImplChannel->epnum), ++ 0, 0); ++ ++ omap_set_dma_dest_params(pImplChannel->sysdma_channel, 0, ++ OMAP_DMA_AMODE_POST_INC, dma_addr, ++ 0, 0); ++ ++ omap_set_dma_dest_data_pack(pImplChannel->sysdma_channel, 1); ++ omap_set_dma_dest_burst_mode(pImplChannel->sysdma_channel, ++ OMAP_DMA_DATA_BURST_16); ++ ++ omap_start_dma(pImplChannel->sysdma_channel); ++ ++ } else { /* Mentor DMA */ ++ if (mode) { ++ csr |= 1 << MUSB_HSDMA_MODE1_SHIFT; ++ BUG_ON(len < packet_sz); + +- if (packet_sz >= 64) { +- csr |= MUSB_HSDMA_BURSTMODE_INCR16 ++ if (packet_sz >= 64) { ++ csr |= MUSB_HSDMA_BURSTMODE_INCR16 + << MUSB_HSDMA_BURSTMODE_SHIFT; +- } else if (packet_sz >= 32) { +- csr |= MUSB_HSDMA_BURSTMODE_INCR8 ++ } else if (packet_sz >= 32) { ++ csr |= MUSB_HSDMA_BURSTMODE_INCR8 + << MUSB_HSDMA_BURSTMODE_SHIFT; +- } else if (packet_sz >= 16) { +- csr |= MUSB_HSDMA_BURSTMODE_INCR4 ++ } else if (packet_sz >= 16) { ++ csr |= MUSB_HSDMA_BURSTMODE_INCR4 + << MUSB_HSDMA_BURSTMODE_SHIFT; ++ } + } +- } + +- csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT) +- | (1 << MUSB_HSDMA_ENABLE_SHIFT) +- | (1 << MUSB_HSDMA_IRQENABLE_SHIFT) +- | (pImplChannel->transmit +- ? (1 << MUSB_HSDMA_TRANSMIT_SHIFT) +- : 0); +- +- /* address/count */ +- musb_writel(mbase, +- MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS), +- dma_addr); +- musb_writel(mbase, +- MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT), +- len); +- +- /* control (this should start things) */ +- musb_writew(mbase, +- MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL), +- csr); ++ csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT) ++ | (1 << MUSB_HSDMA_ENABLE_SHIFT) ++ | (1 << MUSB_HSDMA_IRQENABLE_SHIFT) ++ | (pImplChannel->transmit ++ ? (1 << MUSB_HSDMA_TRANSMIT_SHIFT) ++ : 0); ++ ++ /* address/count */ ++ musb_writel(mbase, ++ MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS), ++ dma_addr); ++ musb_writel(mbase, ++ MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT), ++ len); ++ ++ /* control (this should start things) */ ++ musb_writew(mbase, ++ MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL), ++ csr); ++ } /* Mentor DMA */ + } + + static int dma_channel_program(struct dma_channel *pChannel, +@@ -265,6 +364,12 @@ static int dma_channel_abort(struct dma_ + MUSB_EP_OFFSET(pImplChannel->epnum, MUSB_TXCSR), + csr); + } else { ++ if (pImplChannel->sysdma_channel != -1) { ++ omap_stop_dma(pImplChannel->sysdma_channel); ++ omap_free_dma(pImplChannel->sysdma_channel); ++ pImplChannel->sysdma_channel = -1; ++ } ++ + csr = musb_readw(mbase, + MUSB_EP_OFFSET(pImplChannel->epnum, MUSB_RXCSR)); + csr &= ~(MUSB_RXCSR_AUTOCLEAR | +Index: linux-omap-2.6/drivers/usb/musb/Kconfig +=================================================================== +--- linux-omap-2.6.orig/drivers/usb/musb/Kconfig 2008-07-28 10:54:37.000000000 +0530 ++++ linux-omap-2.6/drivers/usb/musb/Kconfig 2008-08-12 13:18:34.000000000 +0530 +@@ -150,6 +150,14 @@ config USB_INVENTRA_DMA + help + Enable DMA transfers using Mentor's engine. + ++config MUSB_USE_SYSTEM_DMA_RX ++ bool 'Use System DMA for RX endpoints' ++ depends on USB_MUSB_HDRC && USB_INVENTRA_DMA ++ help ++ MUSB RTL version 1.4 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-- +To unsubscribe from this list: send the line "unsubscribe linux-omap" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html |