aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch')
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch417
1 files changed, 417 insertions, 0 deletions
diff --git a/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch b/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch
new file mode 100644
index 0000000000..7d546e10b0
--- /dev/null
+++ b/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0011-musb-fix-isochronous-TXDMA-take-2.patch
@@ -0,0 +1,417 @@
+From 035cd4a26e9b1638b4b0419b98409026176563ca Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Thu, 26 Mar 2009 18:29:19 -0700
+Subject: [PATCH] musb: fix isochronous TXDMA (take 2)
+
+Multi-frame isochronous TX URBs transfers in DMA mode never
+complete with CPPI DMA because musb_host_tx() doesn't restart
+DMA on the second frame, only emitting a debug message.
+With Inventra DMA they complete, but in PIO mode. To fix:
+
+ - Factor out programming of the DMA transfer from
+ musb_ep_program() into musb_tx_dma_program();
+
+ - Reorder the code at the end of musb_host_tx() to
+ facilitate the fallback to PIO iff DMA fails;
+
+ - Handle the buffer offset consistently for both
+ PIO and DMA modes;
+
+ - Add an argument to musb_ep_program() for the same
+ reason (it only worked correctly with non-zero
+ offset of the first frame in PIO mode);
+
+ - Set the completed isochronous frame descriptor's
+ 'actual_length' and 'status' fields correctly in
+ DMA mode.
+
+Also, since CPPI reportedly doesn't like sending isochronous
+packets in the RNDIS mode, change the criterion for this
+mode to be used only for multi-packet transfers. (There's
+no need for that mode in the single-packet case anyway.)
+
+[ dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: split comment paragraph
+into bullet list, shrink patch delta, style tweaks ]
+
+Signed-off-by: Pavel Kiryukhin <pkiryukhin-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
+---
+ drivers/usb/musb/cppi_dma.c | 1 +
+ drivers/usb/musb/musb_host.c | 227 +++++++++++++++++++-----------------------
+ 2 files changed, 105 insertions(+), 123 deletions(-)
+
+diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
+index 569ef0f..ac7227c 100644
+--- a/drivers/usb/musb/cppi_dma.c
++++ b/drivers/usb/musb/cppi_dma.c
+@@ -579,6 +579,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx)
+ * trigger the "send a ZLP?" confusion.
+ */
+ rndis = (maxpacket & 0x3f) == 0
++ && length > maxpacket
+ && length < 0xffff
+ && (length % maxpacket) != 0;
+
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index 6591282..f6e84a0 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -96,8 +96,8 @@
+
+
+ static void musb_ep_program(struct musb *musb, u8 epnum,
+- struct urb *urb, unsigned int nOut,
+- u8 *buf, u32 len);
++ struct urb *urb, int is_out,
++ u8 *buf, u32 offset, u32 len);
+
+ /*
+ * Clear TX fifo. Needed to avoid BABBLE errors.
+@@ -189,9 +189,10 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ {
+ u16 frame;
+ u32 len;
+- void *buf;
+ void __iomem *mbase = musb->mregs;
+ struct urb *urb = next_urb(qh);
++ void *buf = urb->transfer_buffer;
++ u32 offset = 0;
+ struct musb_hw_ep *hw_ep = qh->hw_ep;
+ unsigned pipe = urb->pipe;
+ u8 address = usb_pipedevice(pipe);
+@@ -214,7 +215,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ case USB_ENDPOINT_XFER_ISOC:
+ qh->iso_idx = 0;
+ qh->frame = 0;
+- buf = urb->transfer_buffer + urb->iso_frame_desc[0].offset;
++ offset = urb->iso_frame_desc[0].offset;
+ len = urb->iso_frame_desc[0].length;
+ break;
+ default: /* bulk, interrupt */
+@@ -232,14 +233,14 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ case USB_ENDPOINT_XFER_ISOC: s = "-iso"; break;
+ default: s = "-intr"; break;
+ }; s; }),
+- epnum, buf, len);
++ epnum, buf + offset, len);
+
+ /* Configure endpoint */
+ if (is_in || hw_ep->is_shared_fifo)
+ hw_ep->in_qh = qh;
+ else
+ hw_ep->out_qh = qh;
+- musb_ep_program(musb, epnum, urb, !is_in, buf, len);
++ musb_ep_program(musb, epnum, urb, !is_in, buf, offset, len);
+
+ /* transmit may have more work: start it when it is time */
+ if (is_in)
+@@ -250,7 +251,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
+ case USB_ENDPOINT_XFER_ISOC:
+ case USB_ENDPOINT_XFER_INT:
+ DBG(3, "check whether there's still time for periodic Tx\n");
+- qh->iso_idx = 0;
+ frame = musb_readw(mbase, MUSB_FRAME);
+ /* FIXME this doesn't implement that scheduling policy ...
+ * or handle framecounter wrapping
+@@ -631,14 +631,68 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
+ ep->rx_reinit = 0;
+ }
+
++static bool musb_tx_dma_program(struct dma_controller *dma,
++ struct musb_hw_ep *hw_ep, struct musb_qh *qh,
++ struct urb *urb, u32 offset, u32 length)
++{
++ struct dma_channel *channel = hw_ep->tx_channel;
++ void __iomem *epio = hw_ep->regs;
++ u16 pkt_size = qh->maxpacket;
++ u16 csr;
++ u8 mode;
++
++#ifdef CONFIG_USB_INVENTRA_DMA
++ if (length > channel->max_len)
++ length = channel->max_len;
++
++ csr = musb_readw(epio, MUSB_TXCSR);
++ if (length > pkt_size) {
++ mode = 1;
++ csr |= MUSB_TXCSR_AUTOSET
++ | MUSB_TXCSR_DMAMODE
++ | MUSB_TXCSR_DMAENAB;
++ } else {
++ mode = 0;
++ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
++ csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
++ }
++ channel->desired_mode = mode;
++ musb_writew(epio, MUSB_TXCSR, csr);
++#else
++ if (!is_cppi_enabled() && !tusb_dma_omap())
++ return false;
++
++ channel->actual_len = 0;
++
++ /*
++ * TX uses "RNDIS" mode automatically but needs help
++ * to identify the zero-length-final-packet case.
++ */
++ mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
++#endif
++
++ qh->segsize = length;
++
++ if (!dma->channel_program(channel, pkt_size, mode,
++ urb->transfer_dma + offset, length)) {
++ dma->channel_release(channel);
++ hw_ep->tx_channel = NULL;
++
++ csr = musb_readw(epio, MUSB_TXCSR);
++ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB);
++ musb_writew(epio, MUSB_TXCSR, csr | MUSB_TXCSR_H_WZC_BITS);
++ return false;
++ }
++ return true;
++}
+
+ /*
+ * Program an HDRC endpoint as per the given URB
+ * Context: irqs blocked, controller lock held
+ */
+ static void musb_ep_program(struct musb *musb, u8 epnum,
+- struct urb *urb, unsigned int is_out,
+- u8 *buf, u32 len)
++ struct urb *urb, int is_out,
++ u8 *buf, u32 offset, u32 len)
+ {
+ struct dma_controller *dma_controller;
+ struct dma_channel *dma_channel;
+@@ -764,82 +818,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ else
+ load_count = min((u32) packet_sz, len);
+
+-#ifdef CONFIG_USB_INVENTRA_DMA
+- if (dma_channel) {
+- qh->segsize = min(len, dma_channel->max_len);
+- if (qh->segsize <= packet_sz)
+- dma_channel->desired_mode = 0;
+- else
+- dma_channel->desired_mode = 1;
+-
+- if (dma_channel->desired_mode == 0) {
+- /* Against the programming guide */
+- csr |= (MUSB_TXCSR_DMAENAB);
+- } else
+- csr |= (MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAENAB
+- | MUSB_TXCSR_DMAMODE);
+- musb_writew(epio, MUSB_TXCSR, csr);
+-
+- dma_ok = dma_controller->channel_program(
+- dma_channel, packet_sz,
+- dma_channel->desired_mode,
+- urb->transfer_dma,
+- qh->segsize);
+- if (dma_ok) {
+- load_count = 0;
+- } else {
+- dma_controller->channel_release(dma_channel);
+- if (is_out)
+- hw_ep->tx_channel = NULL;
+- else
+- hw_ep->rx_channel = NULL;
+- dma_channel = NULL;
+-
+- /*
+- * The programming guide says that we must
+- * clear the DMAENAB bit before DMAMODE...
+- */
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_DMAENAB
+- | MUSB_TXCSR_AUTOSET);
+- musb_writew(epio, MUSB_TXCSR, csr);
+- csr &= ~MUSB_TXCSR_DMAMODE;
+- musb_writew(epio, MUSB_TXCSR, csr);
+- }
+- }
+-#endif
+-
+- /* candidate for DMA */
+- if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
+-
+- /* Defer enabling DMA */
+- dma_channel->actual_len = 0L;
+- qh->segsize = len;
+-
+- /* TX uses "rndis" mode automatically, but needs help
+- * to identify the zero-length-final-packet case.
+- */
+- dma_ok = dma_controller->channel_program(
+- dma_channel, packet_sz,
+- (urb->transfer_flags
+- & URB_ZERO_PACKET)
+- == URB_ZERO_PACKET,
+- urb->transfer_dma,
+- qh->segsize);
+- if (dma_ok) {
+- load_count = 0;
+- } else {
+- dma_controller->channel_release(dma_channel);
+- hw_ep->tx_channel = NULL;
+- dma_channel = NULL;
+-
+- /* REVISIT there's an error path here that
+- * needs handling: can't do dma, but
+- * there's no pio buffer address...
+- */
+- }
+- }
++ if (dma_channel && musb_tx_dma_program(dma_controller,
++ hw_ep, qh, urb, offset, len))
++ load_count = 0;
+
+ if (load_count) {
+ /* PIO to load FIFO */
+@@ -899,7 +880,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ dma_channel, packet_sz,
+ !(urb->transfer_flags
+ & URB_SHORT_NOT_OK),
+- urb->transfer_dma,
++ urb->transfer_dma + offset,
+ qh->segsize);
+ if (!dma_ok) {
+ dma_controller->channel_release(
+@@ -1142,8 +1123,8 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ int pipe;
+ bool done = false;
+ u16 tx_csr;
+- size_t wLength = 0;
+- u8 *buf = NULL;
++ size_t length = 0;
++ size_t offset = 0;
+ struct urb *urb;
+ struct musb_hw_ep *hw_ep = musb->endpoints + epnum;
+ void __iomem *epio = hw_ep->regs;
+@@ -1161,7 +1142,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ /* with CPPI, DMA sometimes triggers "extra" irqs */
+ if (!urb) {
+ DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr);
+- goto finish;
++ return;
+ }
+
+ pipe = urb->pipe;
+@@ -1198,7 +1179,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ musb_writew(epio, MUSB_TXCSR,
+ MUSB_TXCSR_H_WZC_BITS
+ | MUSB_TXCSR_TXPKTRDY);
+- goto finish;
++ return;
+ }
+
+ if (status) {
+@@ -1230,29 +1211,28 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ /* second cppi case */
+ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
+ DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr);
+- goto finish;
+-
++ return;
+ }
+
+- /* REVISIT this looks wrong... */
+ if (!status || dma || usb_pipeisoc(pipe)) {
+ if (dma)
+- wLength = dma->actual_len;
++ length = dma->actual_len;
+ else
+- wLength = qh->segsize;
+- qh->offset += wLength;
++ length = qh->segsize;
++ qh->offset += length;
+
+ if (usb_pipeisoc(pipe)) {
+ struct usb_iso_packet_descriptor *d;
+
+ d = urb->iso_frame_desc + qh->iso_idx;
+- d->actual_length = qh->segsize;
++ d->actual_length = length;
++ d->status = status;
+ if (++qh->iso_idx >= urb->number_of_packets) {
+ done = true;
+ } else {
+ d++;
+- buf = urb->transfer_buffer + d->offset;
+- wLength = d->length;
++ offset = d->offset;
++ length = d->length;
+ }
+ } else if (dma) {
+ done = true;
+@@ -1265,10 +1245,8 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ & URB_ZERO_PACKET))
+ done = true;
+ if (!done) {
+- buf = urb->transfer_buffer
+- + qh->offset;
+- wLength = urb->transfer_buffer_length
+- - qh->offset;
++ offset = qh->offset;
++ length = urb->transfer_buffer_length - offset;
+ }
+ }
+ }
+@@ -1287,28 +1265,31 @@ void musb_host_tx(struct musb *musb, u8 epnum)
+ urb->status = status;
+ urb->actual_length = qh->offset;
+ musb_advance_schedule(musb, urb, hw_ep, USB_DIR_OUT);
++ return;
++ } else if (usb_pipeisoc(pipe) && dma) {
++ if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb,
++ offset, length))
++ return;
++ } else if (tx_csr & MUSB_TXCSR_DMAENAB) {
++ DBG(1, "not complete, but DMA enabled?\n");
++ return;
++ }
+
+- } else if (!(tx_csr & MUSB_TXCSR_DMAENAB)) {
+- /* WARN_ON(!buf); */
+-
+- /* REVISIT: some docs say that when hw_ep->tx_double_buffered,
+- * (and presumably, fifo is not half-full) we should write TWO
+- * packets before updating TXCSR ... other docs disagree ...
+- */
+- /* PIO: start next packet in this URB */
+- if (wLength > qh->maxpacket)
+- wLength = qh->maxpacket;
+- musb_write_fifo(hw_ep, wLength, buf);
+- qh->segsize = wLength;
+-
+- musb_ep_select(mbase, epnum);
+- musb_writew(epio, MUSB_TXCSR,
+- MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
+- } else
+- DBG(1, "not complete, but dma enabled?\n");
++ /*
++ * PIO: start next packet in this URB.
++ *
++ * REVISIT: some docs say that when hw_ep->tx_double_buffered,
++ * (and presumably, FIFO is not half-full) we should write *two*
++ * packets before updating TXCSR; other docs disagree...
++ */
++ if (length > qh->maxpacket)
++ length = qh->maxpacket;
++ musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset);
++ qh->segsize = length;
+
+-finish:
+- return;
++ musb_ep_select(mbase, epnum);
++ musb_writew(epio, MUSB_TXCSR,
++ MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
+ }
+
+
+--
+1.6.0.4
+