diff options
Diffstat (limited to 'recipes/linux/linux-omap-pm-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch')
-rw-r--r-- | recipes/linux/linux-omap-pm-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/recipes/linux/linux-omap-pm-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch b/recipes/linux/linux-omap-pm-2.6.29/musb/0010-musb-sanitize-clearing-TXCSR-DMA-bits-take-2.patch new file mode 100644 index 0000000000..bcbe3bbe39 --- /dev/null +++ b/recipes/linux/linux-omap-pm-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 + |