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.

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 <gadi...@ti.com>
Signed-off-by: Ajay Kumar Gupta <ajay.gu...@ti.com>
---
Original version of this patch can be found at,
http://marc.info/?l=linux-usb&m=121861118320955&w=2

 drivers/usb/musb/Kconfig     |    9 ++++
 drivers/usb/musb/musbhsdma.c |  101 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/musb/musbhsdma.h |   10 ++++
 3 files changed, 120 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 07fe490..f847fe2 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
+       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..70342eb 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -36,12 +36,47 @@
 #include <linux/slab.h>
 #include "musb_core.h"
 #include "musbhsdma.h"
+#include <plat/dma.h>
 
 static int dma_controller_start(struct dma_controller *c)
 {
        /* nothing to do */
        return 0;
 }
+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 void dma_channel_release(struct dma_channel *channel);
 
@@ -77,6 +112,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 +131,33 @@ 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 = omap_request_dma(OMAP24XX_DMA_NO_DEVICE,
+                                       "MUSB SysDMA", musb_sysdma_completion,
+                                       (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 +177,12 @@ 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) {
+               omap_stop_dma(musb_channel->sysdma_channel);
+               omap_free_dma(musb_channel->sysdma_channel);
+               musb_channel->sysdma_channel = -1;
+       }
 }
 
 static void configure_channel(struct dma_channel *channel,
@@ -122,12 +191,40 @@ 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) {
+               /* 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);
+
+       } else { /* Mentor DMA */
 
        if (mode) {
                csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
@@ -160,6 +257,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 +312,9 @@ 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)
+                               omap_stop_dma(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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to