summaryrefslogtreecommitdiff
path: root/packages/linux/linux-omap/musb-dmafix.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-omap/musb-dmafix.patch')
-rw-r--r--packages/linux/linux-omap/musb-dmafix.patch275
1 files changed, 275 insertions, 0 deletions
diff --git a/packages/linux/linux-omap/musb-dmafix.patch b/packages/linux/linux-omap/musb-dmafix.patch
new file mode 100644
index 0000000000..259b79be59
--- /dev/null
+++ b/packages/linux/linux-omap/musb-dmafix.patch
@@ -0,0 +1,275 @@
+From: Gadiyar, Anand <gadiyar@ti.com>
+Date: Wed, 13 Aug 2008 07:05:29 +0000 (+0530)
+Subject: MUSB: Workaround for simultaneous TX and RX usage
+X-Git-Url: http://git.mansr.com/?p=linux-omap;a=commitdiff_plain;h=2e6aa4efb0e14c51ff0427927b1b38136911fa93
+
+MUSB: Workaround for simultaneous TX and RX usage
+
+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>
+---
+
+diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
+index a485a86..8583e54 100644
+--- a/drivers/usb/musb/Kconfig
++++ b/drivers/usb/musb/Kconfig
+@@ -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
+diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
+index 32bb1e2..d1c1ea0 100644
+--- a/drivers/usb/musb/musbhsdma.c
++++ b/drivers/usb/musb/musbhsdma.c
+@@ -34,6 +34,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+ #include "musb_core.h"
++#include <asm/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 dma_controller *c)
+ 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_allocate(struct dma_controller *c,
+ /* 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 dma_channel *pChannel)
+ ~(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_channel *pChannel,
+ 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);
+
+- if (packet_sz >= 64) {
+- csr |= MUSB_HSDMA_BURSTMODE_INCR16
++ 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
+ << 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_channel *pChannel)
+ 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 |