| From c418fd6c01fbc5516a2cd1eaf1df1ec86869028a Mon Sep 17 00:00:00 2001 |
| From: Paul Elder <paul.elder@ideasonboard.com> |
| Date: Wed, 30 Jan 2019 08:13:21 -0600 |
| Subject: usb: gadget: musb: fix short isoc packets with inventra dma |
| |
| From: Paul Elder <paul.elder@ideasonboard.com> |
| |
| commit c418fd6c01fbc5516a2cd1eaf1df1ec86869028a upstream. |
| |
| 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: 550a7375fe72 ("USB: Add MUSB and TUSB support") |
| Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> |
| Signed-off-by: Bin Liu <b-liu@ti.com> |
| Cc: stable <stable@vger.kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/musb/musb_gadget.c | 13 +------------ |
| drivers/usb/musb/musbhsdma.c | 21 +++++++++++---------- |
| 2 files changed, 12 insertions(+), 22 deletions(-) |
| |
| --- a/drivers/usb/musb/musb_gadget.c |
| +++ b/drivers/usb/musb/musb_gadget.c |
| @@ -477,13 +477,10 @@ void musb_g_tx(struct musb *musb, u8 epn |
| } |
| |
| if (request) { |
| - u8 is_dma = 0; |
| - bool short_packet = false; |
| |
| trace_musb_req_tx(req); |
| |
| if (dma && (csr & MUSB_TXCSR_DMAENAB)) { |
| - is_dma = 1; |
| csr |= MUSB_TXCSR_P_WZC_BITS; |
| csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN | |
| MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET); |
| @@ -501,16 +498,8 @@ void musb_g_tx(struct musb *musb, u8 epn |
| */ |
| if ((request->zero && request->length) |
| && (request->length % musb_ep->packet_sz == 0) |
| - && (request->actual == request->length)) |
| - short_packet = true; |
| + && (request->actual == request->length)) { |
| |
| - 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 |
| * available yet... |
| --- a/drivers/usb/musb/musbhsdma.c |
| +++ b/drivers/usb/musb/musbhsdma.c |
| @@ -320,12 +320,10 @@ static irqreturn_t dma_controller_irq(in |
| channel->status = MUSB_DMA_STATUS_FREE; |
| |
| /* completed */ |
| - if ((devctl & MUSB_DEVCTL_HM) |
| - && (musb_channel->transmit) |
| - && ((channel->desired_mode == 0) |
| - || (channel->actual_len & |
| - (musb_channel->max_packet_sz - 1))) |
| - ) { |
| + if (musb_channel->transmit && |
| + (!channel->desired_mode || |
| + (channel->actual_len % |
| + musb_channel->max_packet_sz))) { |
| u8 epnum = musb_channel->epnum; |
| int offset = musb->io.ep_offset(epnum, |
| MUSB_TXCSR); |
| @@ -337,11 +335,14 @@ static irqreturn_t dma_controller_irq(in |
| */ |
| musb_ep_select(mbase, epnum); |
| txcsr = musb_readw(mbase, offset); |
| - txcsr &= ~(MUSB_TXCSR_DMAENAB |
| + if (channel->desired_mode == 1) { |
| + txcsr &= ~(MUSB_TXCSR_DMAENAB |
| | MUSB_TXCSR_AUTOSET); |
| - musb_writew(mbase, offset, txcsr); |
| - /* Send out the packet */ |
| - txcsr &= ~MUSB_TXCSR_DMAMODE; |
| + musb_writew(mbase, offset, txcsr); |
| + /* Send out the packet */ |
| + txcsr &= ~MUSB_TXCSR_DMAMODE; |
| + txcsr |= MUSB_TXCSR_DMAENAB; |
| + } |
| txcsr |= MUSB_TXCSR_TXPKTRDY; |
| musb_writew(mbase, offset, txcsr); |
| } |