Commit 527ea73e authored by Kyungmin Park's avatar Kyungmin Park Committed by Greg Kroah-Hartman

USB: device DMA support on OMAP2

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: default avatarKyungmin Park <kyungmin.park@samsung.com>
Cc: David Brownell <david-b@pacbell.net>
Cc: Tony Lindgren <tony@atomide.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 568fdade
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* Copyright (C) 2004 Texas Instruments, Inc. * Copyright (C) 2004 Texas Instruments, Inc.
* Copyright (C) 2004-2005 David Brownell * 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 * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
...@@ -60,11 +62,6 @@ ...@@ -60,11 +62,6 @@
/* bulk DMA seems to be behaving for both IN and OUT */ /* bulk DMA seems to be behaving for both IN and OUT */
#define USE_DMA #define USE_DMA
/* FIXME: OMAP2 currently has some problem in DMA mode */
#ifdef CONFIG_ARCH_OMAP2
#undef USE_DMA
#endif
/* ISO too */ /* ISO too */
#define USE_ISO #define USE_ISO
...@@ -73,6 +70,8 @@ ...@@ -73,6 +70,8 @@
#define DMA_ADDR_INVALID (~(dma_addr_t)0) #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 * 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) ...@@ -571,20 +570,25 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
const int sync_mode = cpu_is_omap15xx() const int sync_mode = cpu_is_omap15xx()
? OMAP_DMA_SYNC_FRAME ? OMAP_DMA_SYNC_FRAME
: OMAP_DMA_SYNC_ELEMENT; : 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 */ /* measure length in either bytes or packets */
if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC) if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC)
|| (cpu_is_omap24xx() && length < ep->maxpacket)
|| (cpu_is_omap15xx() && length < ep->maxpacket)) { || (cpu_is_omap15xx() && length < ep->maxpacket)) {
txdma_ctrl = UDC_TXN_EOT | length; txdma_ctrl = UDC_TXN_EOT | length;
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, 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 { } else {
length = min(length / ep->maxpacket, length = min(length / ep->maxpacket,
(unsigned) UDC_TXN_TSC + 1); (unsigned) UDC_TXN_TSC + 1);
txdma_ctrl = length; txdma_ctrl = length;
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16,
ep->ep.maxpacket >> 1, length, sync_mode, ep->ep.maxpacket >> 1, length, sync_mode,
0, 0); dma_trigger, 0);
length *= ep->maxpacket; length *= ep->maxpacket;
} }
omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, 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) ...@@ -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) 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 /* NOTE: we filtered out "short reads" before, so we know
* the buffer has only whole numbers of packets. * the buffer has only whole numbers of packets.
* except MODE SELECT(6) sent the 24 bytes data in OMAP24XX DMA mode
*/ */
if (cpu_is_omap24xx() && packets < ep->maxpacket) {
/* set up this DMA transfer, enable the fifo, start */ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8,
packets = (req->req.length - req->req.actual) / ep->ep.maxpacket; packets, 1, OMAP_DMA_SYNC_ELEMENT,
packets = min(packets, (unsigned)UDC_RXN_TC + 1); dma_trigger, 0);
req->dma_bytes = packets * ep->ep.maxpacket; req->dma_bytes = packets;
omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, } else {
ep->ep.maxpacket >> 1, packets, /* set up this DMA transfer, enable the fifo, start */
OMAP_DMA_SYNC_ELEMENT, packets /= ep->ep.maxpacket;
0, 0); 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_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
0, 0); 0, 0);
...@@ -743,6 +758,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ...@@ -743,6 +758,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
{ {
u16 reg; u16 reg;
int status, restart, is_in; int status, restart, is_in;
int dma_channel;
is_in = ep->bEndpointAddress & USB_DIR_IN; is_in = ep->bEndpointAddress & USB_DIR_IN;
if (is_in) if (is_in)
...@@ -769,11 +785,15 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ...@@ -769,11 +785,15 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
ep->dma_channel = channel; ep->dma_channel = channel;
if (is_in) { 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); ep->ep.name, dma_error, ep, &ep->lch);
if (status == 0) { if (status == 0) {
UDC_TXDMA_CFG_REG = reg; UDC_TXDMA_CFG_REG = reg;
/* EMIFF */ /* EMIFF or SDRC */
omap_set_dma_src_burst_mode(ep->lch, omap_set_dma_src_burst_mode(ep->lch,
OMAP_DMA_DATA_BURST_4); OMAP_DMA_DATA_BURST_4);
omap_set_dma_src_data_pack(ep->lch, 1); omap_set_dma_src_data_pack(ep->lch, 1);
...@@ -785,7 +805,12 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ...@@ -785,7 +805,12 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
0, 0); 0, 0);
} }
} else { } 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); ep->ep.name, dma_error, ep, &ep->lch);
if (status == 0) { if (status == 0) {
UDC_RXDMA_CFG_REG = reg; UDC_RXDMA_CFG_REG = reg;
...@@ -795,7 +820,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ...@@ -795,7 +820,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
OMAP_DMA_AMODE_CONSTANT, OMAP_DMA_AMODE_CONSTANT,
(unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG),
0, 0); 0, 0);
/* EMIFF */ /* EMIFF or SDRC */
omap_set_dma_dest_burst_mode(ep->lch, omap_set_dma_dest_burst_mode(ep->lch,
OMAP_DMA_DATA_BURST_4); OMAP_DMA_DATA_BURST_4);
omap_set_dma_dest_data_pack(ep->lch, 1); omap_set_dma_dest_data_pack(ep->lch, 1);
...@@ -808,7 +833,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ...@@ -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); omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);
/* channel type P: hw synch (fifo) */ /* 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; 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) ...@@ -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 /* this isn't bogus, but OMAP DMA isn't the only hardware to
* have a hard time with partial packet reads... reject it. * have a hard time with partial packet reads... reject it.
* Except OMAP2 can handle the small packets.
*/ */
if (use_dma if (use_dma
&& ep->has_dma && ep->has_dma
&& ep->bEndpointAddress != 0 && ep->bEndpointAddress != 0
&& (ep->bEndpointAddress & USB_DIR_IN) == 0 && (ep->bEndpointAddress & USB_DIR_IN) == 0
&& !cpu_class_is_omap2()
&& (req->req.length % ep->ep.maxpacket) != 0) { && (req->req.length % ep->ep.maxpacket) != 0) {
DBG("%s, no partial packet OUT reads\n", __FUNCTION__); DBG("%s, no partial packet OUT reads\n", __FUNCTION__);
return -EMSGSIZE; return -EMSGSIZE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment