Commit c418fd6c authored by Paul Elder's avatar Paul Elder Committed by Greg Kroah-Hartman

usb: gadget: musb: fix short isoc packets with inventra dma

Handling short packets (length < max packet size) in the Inventra DMA
engine in the MUSB driver causes the MUSB DMA controller to hang. An
example of a problem that is caused by this problem is when streaming
video out of a UVC gadget, only the first video frame is transferred.

For short packets (mode-0 or mode-1 DMA), MUSB_TXCSR_TXPKTRDY must be
set manually by the driver. This was previously done in musb_g_tx
(musb_gadget.c), but incorrectly (all csr flags were cleared, and only
MUSB_TXCSR_MODE and MUSB_TXCSR_TXPKTRDY were set). Fixing that problem
allows some requests to be transferred correctly, but multiple requests
were often put together in one USB packet, and caused problems if the
packet size was not a multiple of 4. Instead, set MUSB_TXCSR_TXPKTRDY
in dma_controller_irq (musbhsdma.c), just like host mode transfers.

This topic was originally tackled by Nicolas Boichat [0] [1] and is
discussed further at [2] as part of his GSoC project [3].

[0] https://groups.google.com/forum/?hl=en#!topic/beagleboard-gsoc/k8Azwfp75CU
[1] https://gitorious.org/beagleboard-usbsniffer/beagleboard-usbsniffer-kernel/commit/b0be3b6cc195ba732189b04f1d43ec843c3e54c9?p=beagleboard-usbsniffer:beagleboard-usbsniffer-kernel.git;a=patch;h=b0be3b6cc195ba732189b04f1d43ec843c3e54c9
[2] http://beagleboard-usbsniffer.blogspot.com/2010/07/musb-isochronous-transfers-fixed.html
[3] http://elinux.org/BeagleBoard/GSoC/USBSniffer

Fixes: 550a7375 ("USB: Add MUSB and TUSB support")
Signed-off-by: default avatarPaul Elder <paul.elder@ideasonboard.com>
Signed-off-by: default avatarBin Liu <b-liu@ti.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f17b5f06
...@@ -452,13 +452,10 @@ void musb_g_tx(struct musb *musb, u8 epnum) ...@@ -452,13 +452,10 @@ void musb_g_tx(struct musb *musb, u8 epnum)
} }
if (request) { if (request) {
u8 is_dma = 0;
bool short_packet = false;
trace_musb_req_tx(req); trace_musb_req_tx(req);
if (dma && (csr & MUSB_TXCSR_DMAENAB)) { if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
is_dma = 1;
csr |= MUSB_TXCSR_P_WZC_BITS; csr |= MUSB_TXCSR_P_WZC_BITS;
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN | csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET); MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET);
...@@ -476,16 +473,8 @@ void musb_g_tx(struct musb *musb, u8 epnum) ...@@ -476,16 +473,8 @@ void musb_g_tx(struct musb *musb, u8 epnum)
*/ */
if ((request->zero && request->length) if ((request->zero && request->length)
&& (request->length % musb_ep->packet_sz == 0) && (request->length % musb_ep->packet_sz == 0)
&& (request->actual == request->length)) && (request->actual == request->length)) {
short_packet = true;
if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) &&
(is_dma && (!dma->desired_mode ||
(request->actual &
(musb_ep->packet_sz - 1)))))
short_packet = true;
if (short_packet) {
/* /*
* On DMA completion, FIFO may not be * On DMA completion, FIFO may not be
* available yet... * available yet...
......
...@@ -346,12 +346,10 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) ...@@ -346,12 +346,10 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
channel->status = MUSB_DMA_STATUS_FREE; channel->status = MUSB_DMA_STATUS_FREE;
/* completed */ /* completed */
if ((devctl & MUSB_DEVCTL_HM) if (musb_channel->transmit &&
&& (musb_channel->transmit) (!channel->desired_mode ||
&& ((channel->desired_mode == 0) (channel->actual_len %
|| (channel->actual_len & musb_channel->max_packet_sz))) {
(musb_channel->max_packet_sz - 1)))
) {
u8 epnum = musb_channel->epnum; u8 epnum = musb_channel->epnum;
int offset = musb->io.ep_offset(epnum, int offset = musb->io.ep_offset(epnum,
MUSB_TXCSR); MUSB_TXCSR);
...@@ -363,11 +361,14 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) ...@@ -363,11 +361,14 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
*/ */
musb_ep_select(mbase, epnum); musb_ep_select(mbase, epnum);
txcsr = musb_readw(mbase, offset); txcsr = musb_readw(mbase, offset);
if (channel->desired_mode == 1) {
txcsr &= ~(MUSB_TXCSR_DMAENAB txcsr &= ~(MUSB_TXCSR_DMAENAB
| MUSB_TXCSR_AUTOSET); | MUSB_TXCSR_AUTOSET);
musb_writew(mbase, offset, txcsr); musb_writew(mbase, offset, txcsr);
/* Send out the packet */ /* Send out the packet */
txcsr &= ~MUSB_TXCSR_DMAMODE; txcsr &= ~MUSB_TXCSR_DMAMODE;
txcsr |= MUSB_TXCSR_DMAENAB;
}
txcsr |= MUSB_TXCSR_TXPKTRDY; txcsr |= MUSB_TXCSR_TXPKTRDY;
musb_writew(mbase, offset, txcsr); musb_writew(mbase, offset, txcsr);
} }
......
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