Re: [PATCH] MUSB: Workaround for simultaneous TX and RX usage

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Aug 4, 2008 at 9:18 PM, Gadiyar, Anand <gadiyar@xxxxxx> wrote:
> 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.
>

Hi Gadiyar,

We also found some similar issue on Blackfin with the MUSB+musbhsdma.
Some time DMA mode 1 interrupt lost and system hang.

But if we use the PIO mode and rewrite the
musb_read_fifo/musb_write_fifo to use DMA polling method,
the issue is gone.

Any idea about this?

-Bryan

> 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.
>
> Signed-off-by: Anand Gadiyar <gadiyar@xxxxxx>
> ---
> This issue was first reported by Jon Hunter on [1]
>
> [1] http://marc.info/?l=linux-omap&m=119634480534453&w=2
>
>  drivers/usb/musb/Kconfig     |    8 +
>  drivers/usb/musb/musbhsdma.c |  174 +++++++++++++++++++++++++++++++++++--------
>  2 files changed, 153 insertions(+), 29 deletions(-)
>
> Index: testing/drivers/usb/musb/musbhsdma.c
> ===================================================================
> --- testing.orig/drivers/usb/musb/musbhsdma.c
> +++ testing/drivers/usb/musb/musbhsdma.c
> @@ -34,6 +34,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/platform_device.h>
>  #include "musb_core.h"
> +#include <asm/arch/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,10 @@ struct musb_dma_channel {
>        u8                              bIndex;
>        u8                              epnum;
>        u8                              transmit;
> +
> +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
> +       int                             sysdma_channel;
> +#endif
>  };
>
>  struct musb_dma_controller {
> @@ -93,6 +101,10 @@ 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);
> +#endif
> +
>  static void dma_channel_release(struct dma_channel *pChannel);
>
>  static int dma_controller_stop(struct dma_controller *c)
> @@ -144,6 +156,30 @@ 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));
> +/* FIXME: Decide on the correct private data to use */
> +
> +                               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 +199,14 @@ static void dma_channel_release(struct d
>                ~(1 << pImplChannel->bIndex);
>
>        pChannel->status = MUSB_DMA_STATUS_UNKNOWN;
> +
> +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
> +       if (pImplChannel->sysdma_channel != -1) {
> +               omap_stop_dma(pImplChannel->sysdma_channel);
> +               omap_free_dma(pImplChannel->sysdma_channel);
> +               pImplChannel->sysdma_channel = -1;
> +       }
> +#endif
>  }
>
>  static void configure_channel(struct dma_channel *pChannel,
> @@ -179,43 +223,108 @@ 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);
> +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
> +       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);
>
> -               if (packet_sz >= 64) {
> -                       csr |= MUSB_HSDMA_BURSTMODE_INCR16
> +               omap_start_dma(pImplChannel->sysdma_channel);
> +
> +       } else
> +#endif
> +       { /* 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 */
>  }
>
> +#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;
> +}
> +#endif
> +
>  static int dma_channel_program(struct dma_channel *pChannel,
>                                u16 packet_sz, u8 mode,
>                                dma_addr_t dma_addr, u32 len)
> @@ -265,6 +374,13 @@ static int dma_channel_abort(struct dma_
>                                MUSB_EP_OFFSET(pImplChannel->epnum, MUSB_TXCSR),
>                                csr);
>                } else {
> +#ifdef CONFIG_MUSB_USE_SYSTEM_DMA_RX
> +                       if (pImplChannel->sysdma_channel != -1) {
> +                               omap_stop_dma(pImplChannel->sysdma_channel);
> +                               omap_free_dma(pImplChannel->sysdma_channel);
> +                               pImplChannel->sysdma_channel = -1;
> +                       }
> +#endif
>                        csr = musb_readw(mbase,
>                                MUSB_EP_OFFSET(pImplChannel->epnum, MUSB_RXCSR));
>                        csr &= ~(MUSB_RXCSR_AUTOCLEAR |
> Index: testing/drivers/usb/musb/Kconfig
> ===================================================================
> --- testing.orig/drivers/usb/musb/Kconfig
> +++ testing/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
> --
> 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
>
--
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

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux