USB: device DMA support on OMAP2
authorKyungmin Park <kyungmin.park@samsung.com>
Wed, 21 Nov 2007 23:13:15 +0000 (15:13 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 1 Feb 2008 22:34:48 +0000 (14:34 -0800)
The current omap udc dosen't support the DMA mode and it has some problem
at setup time on OMAP2 with previous patch file.  I found that the code
assumes bulk out required the big data transfer.  But MODE SELECT(6) sent
the only 24 bytes.  it makes a problem.  So I implement the small packets
handling for it.

It is tested with both linux and windows.

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: David Brownell <david-b@pacbell.net>
Cc: Tony Lindgren <tony@atomide.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/omap_udc.c

index d377154..7d6bef5 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright (C) 2004 Texas Instruments, Inc.
  * Copyright (C) 2004-2005 David Brownell
  *
+ * OMAP2 & DMA support by Kyungmin Park <kyungmin.park@samsung.com>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
 /* bulk DMA seems to be behaving for both IN and OUT */
 #define        USE_DMA
 
-/* FIXME: OMAP2 currently has some problem in DMA mode */
-#ifdef CONFIG_ARCH_OMAP2
-#undef USE_DMA
-#endif
-
 /* ISO too */
 #define        USE_ISO
 
@@ -73,6 +70,8 @@
 
 #define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
 
+#define OMAP2_DMA_CH(ch)       (((ch) - 1) << 1)
+#define OMAP24XX_DMA(name, ch) (OMAP24XX_DMA_##name + OMAP2_DMA_CH(ch))
 
 /*
  * The OMAP UDC needs _very_ early endpoint setup:  before enabling the
@@ -571,20 +570,25 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
        const int       sync_mode = cpu_is_omap15xx()
                                ? OMAP_DMA_SYNC_FRAME
                                : OMAP_DMA_SYNC_ELEMENT;
+       int             dma_trigger = 0;
+
+       if (cpu_is_omap24xx())
+               dma_trigger = OMAP24XX_DMA(USB_W2FC_TX0, ep->dma_channel);
 
        /* measure length in either bytes or packets */
        if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
+                       || (cpu_is_omap24xx() && length < ep->maxpacket)
                        || (cpu_is_omap15xx() && length < ep->maxpacket)) {
                txdma_ctrl = UDC_TXN_EOT | length;
                omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
-                               length, 1, sync_mode, 0, 0);
+                               length, 1, sync_mode, dma_trigger, 0);
        } else {
                length = min(length / ep->maxpacket,
                                (unsigned) UDC_TXN_TSC + 1);
                txdma_ctrl = length;
                omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
                                ep->ep.maxpacket >> 1, length, sync_mode,
-                               0, 0);
+                               dma_trigger, 0);
                length *= ep->maxpacket;
        }
        omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF,
@@ -622,20 +626,31 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status)
 
 static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
 {
-       unsigned packets;
+       unsigned packets = req->req.length - req->req.actual;
+       int dma_trigger = 0;
+
+       if (cpu_is_omap24xx())
+               dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel);
 
        /* NOTE:  we filtered out "short reads" before, so we know
         * the buffer has only whole numbers of packets.
+        * except MODE SELECT(6) sent the 24 bytes data in OMAP24XX DMA mode
         */
-
-       /* set up this DMA transfer, enable the fifo, start */
-       packets = (req->req.length - req->req.actual) / ep->ep.maxpacket;
-       packets = min(packets, (unsigned)UDC_RXN_TC + 1);
-       req->dma_bytes = packets * ep->ep.maxpacket;
-       omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
-                       ep->ep.maxpacket >> 1, packets,
-                       OMAP_DMA_SYNC_ELEMENT,
-                       0, 0);
+       if (cpu_is_omap24xx() && packets < ep->maxpacket) {
+               omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
+                               packets, 1, OMAP_DMA_SYNC_ELEMENT,
+                               dma_trigger, 0);
+               req->dma_bytes = packets;
+       } else {
+               /* set up this DMA transfer, enable the fifo, start */
+               packets /= ep->ep.maxpacket;
+               packets = min(packets, (unsigned)UDC_RXN_TC + 1);
+               req->dma_bytes = packets * ep->ep.maxpacket;
+               omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
+                               ep->ep.maxpacket >> 1, packets,
+                               OMAP_DMA_SYNC_ELEMENT,
+                               dma_trigger, 0);
+       }
        omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
                OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
                0, 0);
@@ -743,6 +758,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
 {
        u16     reg;
        int     status, restart, is_in;
+       int     dma_channel;
 
        is_in = ep->bEndpointAddress & USB_DIR_IN;
        if (is_in)
@@ -769,11 +785,15 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
        ep->dma_channel = channel;
 
        if (is_in) {
-               status = omap_request_dma(OMAP_DMA_USB_W2FC_TX0 - 1 + channel,
+               if (cpu_is_omap24xx())
+                       dma_channel = OMAP24XX_DMA(USB_W2FC_TX0, channel);
+               else
+                       dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel;
+               status = omap_request_dma(dma_channel,
                        ep->ep.name, dma_error, ep, &ep->lch);
                if (status == 0) {
                        UDC_TXDMA_CFG_REG = reg;
-                       /* EMIFF */
+                       /* EMIFF or SDRC */
                        omap_set_dma_src_burst_mode(ep->lch,
                                                OMAP_DMA_DATA_BURST_4);
                        omap_set_dma_src_data_pack(ep->lch, 1);
@@ -785,7 +805,12 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                                0, 0);
                }
        } else {
-               status = omap_request_dma(OMAP_DMA_USB_W2FC_RX0 - 1 + channel,
+               if (cpu_is_omap24xx())
+                       dma_channel = OMAP24XX_DMA(USB_W2FC_RX0, channel);
+               else
+                       dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel;
+
+               status = omap_request_dma(dma_channel,
                        ep->ep.name, dma_error, ep, &ep->lch);
                if (status == 0) {
                        UDC_RXDMA_CFG_REG = reg;
@@ -795,7 +820,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                                OMAP_DMA_AMODE_CONSTANT,
                                (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG),
                                0, 0);
-                       /* EMIFF */
+                       /* EMIFF or SDRC */
                        omap_set_dma_dest_burst_mode(ep->lch,
                                                OMAP_DMA_DATA_BURST_4);
                        omap_set_dma_dest_data_pack(ep->lch, 1);
@@ -808,7 +833,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
                omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);
 
                /* channel type P: hw synch (fifo) */
-               if (!cpu_is_omap15xx())
+               if (cpu_class_is_omap1() && !cpu_is_omap15xx())
                        OMAP1_DMA_LCH_CTRL_REG(ep->lch) = 2;
        }
 
@@ -926,11 +951,13 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 
        /* this isn't bogus, but OMAP DMA isn't the only hardware to
         * have a hard time with partial packet reads...  reject it.
+        * Except OMAP2 can handle the small packets.
         */
        if (use_dma
                        && ep->has_dma
                        && ep->bEndpointAddress != 0
                        && (ep->bEndpointAddress & USB_DIR_IN) == 0
+                       && !cpu_class_is_omap2()
                        && (req->req.length % ep->ep.maxpacket) != 0) {
                DBG("%s, no partial packet OUT reads\n", __FUNCTION__);
                return -EMSGSIZE;