aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch')
-rw-r--r--meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch361
1 files changed, 361 insertions, 0 deletions
diff --git a/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch b/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch
new file mode 100644
index 0000000000..bcbe3bbe39
--- /dev/null
+++ b/meta/recipes-kernel/linux/linux-omap-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch
@@ -0,0 +1,361 @@
+From c99f4a68268801a2e2ffbef9766c3ac89e4fb22c Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
+Date: Thu, 26 Mar 2009 18:27:47 -0700
+Subject: [PATCH] musb: sanitize clearing TXCSR DMA bits (take 2)
+
+The MUSB code clears TXCSR_DMAMODE incorrectly in several
+places, either asserting that TXCSR_DMAENAB is clear (when
+sometimes it isn't) or clearing both bits together. Recent
+versions of the programmer's guide require DMAENAB to be
+cleared first, although some older ones didn't.
+
+Fix this and while at it:
+
+ - In musb_gadget::txstate(), stop clearing the AUTOSET
+ and DMAMODE bits for the CPPI case since they never
+ get set anyway (the former bit is reserved on DaVinci);
+ but do clear the DMAENAB bit on the DMA error path.
+
+ - In musb_host::musb_ep_program(), remove the duplicate
+ DMA controller specific code code clearing the TXCSR
+ previous state, add the code to clear TXCSR DMA bits
+ on the Inventra DMA error path, to replace such code
+ (executed late) on the PIO path.
+
+ - In musbhsdma::dma_channel_abort()/dma_controller_irq(),
+ add/use the 'offset' variable to avoid MUSB_EP_OFFSET()
+ invocations on every RXCSR/TXCSR access.
+
+[dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org: don't introduce CamelCase,
+shrink diff]
+
+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/musb_gadget.c | 33 +++++++++++------
+ drivers/usb/musb/musb_host.c | 79 ++++++++++++++++------------------------
+ drivers/usb/musb/musbhsdma.c | 59 ++++++++++++++++++------------
+ 3 files changed, 90 insertions(+), 81 deletions(-)
+
+diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
+index c7ebd08..f79440c 100644
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -165,9 +165,15 @@ static void nuke(struct musb_ep *ep, const int status)
+ if (is_dma_capable() && ep->dma) {
+ struct dma_controller *c = ep->musb->dma_controller;
+ int value;
++
+ if (ep->is_in) {
++ /*
++ * The programming guide says that we must not clear
++ * the DMAMODE bit before DMAENAB, so we only
++ * clear it in the second write...
++ */
+ musb_writew(epio, MUSB_TXCSR,
+- 0 | MUSB_TXCSR_FLUSHFIFO);
++ MUSB_TXCSR_DMAMODE | MUSB_TXCSR_FLUSHFIFO);
+ musb_writew(epio, MUSB_TXCSR,
+ 0 | MUSB_TXCSR_FLUSHFIFO);
+ } else {
+@@ -230,7 +236,7 @@ static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep)
+ | IN token(s) are recd from Host.
+ | -> DMA interrupt on completion
+ | calls TxAvail.
+- | -> stop DMA, ~DmaEenab,
++ | -> stop DMA, ~DMAENAB,
+ | -> set TxPktRdy for last short pkt or zlp
+ | -> Complete Request
+ | -> Continue next request (call txstate)
+@@ -315,9 +321,17 @@ static void txstate(struct musb *musb, struct musb_request *req)
+ request->dma, request_size);
+ if (use_dma) {
+ if (musb_ep->dma->desired_mode == 0) {
+- /* ASSERT: DMAENAB is clear */
+- csr &= ~(MUSB_TXCSR_AUTOSET |
+- MUSB_TXCSR_DMAMODE);
++ /*
++ * We must not clear the DMAMODE bit
++ * before the DMAENAB bit -- and the
++ * latter doesn't always get cleared
++ * before we get here...
++ */
++ csr &= ~(MUSB_TXCSR_AUTOSET
++ | MUSB_TXCSR_DMAENAB);
++ musb_writew(epio, MUSB_TXCSR, csr
++ | MUSB_TXCSR_P_WZC_BITS);
++ csr &= ~MUSB_TXCSR_DMAMODE;
+ csr |= (MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_MODE);
+ /* against programming guide */
+@@ -334,10 +348,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
+
+ #elif defined(CONFIG_USB_TI_CPPI_DMA)
+ /* program endpoint CSR first, then setup DMA */
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_P_UNDERRUN
+- | MUSB_TXCSR_TXPKTRDY);
++ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
+ csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_DMAENAB;
+ musb_writew(epio, MUSB_TXCSR,
+ (MUSB_TXCSR_P_WZC_BITS & ~MUSB_TXCSR_P_UNDERRUN)
+@@ -364,8 +375,8 @@ static void txstate(struct musb *musb, struct musb_request *req)
+ if (!use_dma) {
+ c->channel_release(musb_ep->dma);
+ musb_ep->dma = NULL;
+- /* ASSERT: DMAENAB clear */
+- csr &= ~(MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE);
++ csr &= ~MUSB_TXCSR_DMAENAB;
++ musb_writew(epio, MUSB_TXCSR, csr);
+ /* invariant: prequest->buf is non-null */
+ }
+ #elif defined(CONFIG_USB_TUSB_OMAP_DMA)
+diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
+index a5d75aa..6591282 100644
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -590,10 +590,17 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
+ csr = musb_readw(ep->regs, MUSB_TXCSR);
+ if (csr & MUSB_TXCSR_MODE) {
+ musb_h_tx_flush_fifo(ep);
++ csr = musb_readw(ep->regs, MUSB_TXCSR);
+ musb_writew(ep->regs, MUSB_TXCSR,
+- MUSB_TXCSR_FRCDATATOG);
++ csr | MUSB_TXCSR_FRCDATATOG);
+ }
+- /* clear mode (and everything else) to enable Rx */
++
++ /*
++ * Clear the MODE bit (and everything else) to enable Rx.
++ * NOTE: we mustn't clear the DMAMODE bit before DMAENAB.
++ */
++ if (csr & MUSB_TXCSR_DMAMODE)
++ musb_writew(ep->regs, MUSB_TXCSR, MUSB_TXCSR_DMAMODE);
+ musb_writew(ep->regs, MUSB_TXCSR, 0);
+
+ /* scrub all previous state, clearing toggle */
+@@ -690,12 +697,17 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+
+ /* general endpoint setup */
+ if (epnum) {
+- /* ASSERT: TXCSR_DMAENAB was already cleared */
+-
+ /* flush all old state, set default */
+ musb_h_tx_flush_fifo(hw_ep);
++
++ /*
++ * We must not clear the DMAMODE bit before or in
++ * the same cycle with the DMAENAB bit, so we clear
++ * the latter first...
++ */
+ csr &= ~(MUSB_TXCSR_H_NAKTIMEOUT
+- | MUSB_TXCSR_DMAMODE
++ | MUSB_TXCSR_AUTOSET
++ | MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_FRCDATATOG
+ | MUSB_TXCSR_H_RXSTALL
+ | MUSB_TXCSR_H_ERROR
+@@ -703,16 +715,15 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ );
+ csr |= MUSB_TXCSR_MODE;
+
+- if (usb_gettoggle(urb->dev,
+- qh->epnum, 1))
++ if (usb_gettoggle(urb->dev, qh->epnum, 1))
+ csr |= MUSB_TXCSR_H_WR_DATATOGGLE
+ | MUSB_TXCSR_H_DATATOGGLE;
+ else
+ csr |= MUSB_TXCSR_CLRDATATOG;
+
+- /* twice in case of double packet buffering */
+ musb_writew(epio, MUSB_TXCSR, csr);
+ /* REVISIT may need to clear FLUSHFIFO ... */
++ csr &= ~MUSB_TXCSR_DMAMODE;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ csr = musb_readw(epio, MUSB_TXCSR);
+ } else {
+@@ -755,34 +766,19 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+
+ #ifdef CONFIG_USB_INVENTRA_DMA
+ if (dma_channel) {
+-
+- /* clear previous state */
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_DMAENAB);
+- csr |= MUSB_TXCSR_MODE;
+- musb_writew(epio, MUSB_TXCSR,
+- csr | MUSB_TXCSR_MODE);
+-
+ 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) {
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE);
++ /* Against the programming guide */
+ csr |= (MUSB_TXCSR_DMAENAB);
+- /* against programming guide */
+ } else
+ csr |= (MUSB_TXCSR_AUTOSET
+ | MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_DMAMODE);
+-
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ dma_ok = dma_controller->channel_program(
+@@ -799,6 +795,17 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ 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
+@@ -806,18 +813,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ /* candidate for DMA */
+ if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
+
+- /* program endpoint CSRs first, then setup DMA.
+- * assume CPPI setup succeeds.
+- * defer enabling dma.
+- */
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_AUTOSET
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_DMAENAB);
+- csr |= MUSB_TXCSR_MODE;
+- musb_writew(epio, MUSB_TXCSR,
+- csr | MUSB_TXCSR_MODE);
+-
++ /* Defer enabling DMA */
+ dma_channel->actual_len = 0L;
+ qh->segsize = len;
+
+@@ -846,20 +842,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
+ }
+
+ if (load_count) {
+- /* ASSERT: TXCSR_DMAENAB was already cleared */
+-
+ /* PIO to load FIFO */
+ qh->segsize = load_count;
+ musb_write_fifo(hw_ep, load_count, buf);
+- csr = musb_readw(epio, MUSB_TXCSR);
+- csr &= ~(MUSB_TXCSR_DMAENAB
+- | MUSB_TXCSR_DMAMODE
+- | MUSB_TXCSR_AUTOSET);
+- /* write CSR */
+- csr |= MUSB_TXCSR_MODE;
+-
+- if (epnum)
+- musb_writew(epio, MUSB_TXCSR, csr);
+ }
+
+ /* re-enable interrupt */
+diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
+index 8662e9e..40709c3 100644
+--- a/drivers/usb/musb/musbhsdma.c
++++ b/drivers/usb/musb/musbhsdma.c
+@@ -195,30 +195,32 @@ static int dma_channel_abort(struct dma_channel *channel)
+ void __iomem *mbase = musb_channel->controller->base;
+
+ u8 bchannel = musb_channel->idx;
++ int offset;
+ u16 csr;
+
+ if (channel->status == MUSB_DMA_STATUS_BUSY) {
+ if (musb_channel->transmit) {
+-
+- csr = musb_readw(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum,
+- MUSB_TXCSR));
+- csr &= ~(MUSB_TXCSR_AUTOSET |
+- MUSB_TXCSR_DMAENAB |
+- MUSB_TXCSR_DMAMODE);
+- musb_writew(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum, MUSB_TXCSR),
+- csr);
++ offset = MUSB_EP_OFFSET(musb_channel->epnum,
++ MUSB_TXCSR);
++
++ /*
++ * The programming guide says that we must clear
++ * the DMAENAB bit before the DMAMODE bit...
++ */
++ csr = musb_readw(mbase, offset);
++ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB);
++ musb_writew(mbase, offset, csr);
++ csr &= ~MUSB_TXCSR_DMAMODE;
++ musb_writew(mbase, offset, csr);
+ } else {
+- csr = musb_readw(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum,
+- MUSB_RXCSR));
++ offset = MUSB_EP_OFFSET(musb_channel->epnum,
++ MUSB_RXCSR);
++
++ csr = musb_readw(mbase, offset);
+ csr &= ~(MUSB_RXCSR_AUTOCLEAR |
+ MUSB_RXCSR_DMAENAB |
+ MUSB_RXCSR_DMAMODE);
+- musb_writew(mbase,
+- MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR),
+- csr);
++ musb_writew(mbase, offset, csr);
+ }
+
+ musb_writew(mbase,
+@@ -296,14 +298,25 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
+ && ((channel->desired_mode == 0)
+ || (channel->actual_len &
+ (musb_channel->max_packet_sz - 1)))
+- ) {
++ ) {
++ u8 epnum = musb_channel->epnum;
++ int offset = MUSB_EP_OFFSET(epnum,
++ MUSB_TXCSR);
++ u16 txcsr;
++
++ /*
++ * The programming guide says that we
++ * must clear DMAENAB before DMAMODE.
++ */
++ musb_ep_select(mbase, epnum);
++ txcsr = musb_readw(mbase, offset);
++ txcsr &= ~(MUSB_TXCSR_DMAENAB
++ | MUSB_TXCSR_AUTOSET);
++ musb_writew(mbase, offset, txcsr);
+ /* Send out the packet */
+- musb_ep_select(mbase,
+- musb_channel->epnum);
+- musb_writew(mbase, MUSB_EP_OFFSET(
+- musb_channel->epnum,
+- MUSB_TXCSR),
+- MUSB_TXCSR_TXPKTRDY);
++ txcsr &= ~MUSB_TXCSR_DMAMODE;
++ txcsr |= MUSB_TXCSR_TXPKTRDY;
++ musb_writew(mbase, offset, txcsr);
+ } else {
+ musb_dma_completion(
+ musb,
+--
+1.6.0.4
+