Skip to content

Commit

Permalink
USB: musb: sanitize clearing TXCSR DMA bits (take 2)
Browse files Browse the repository at this point in the history
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@users.sourceforge.net: don't introduce CamelCase,
shrink diff]

Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Sergei Shtylyov authored and Greg Kroah-Hartman committed Apr 17, 2009
1 parent c7bbc05 commit b6e434a
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 81 deletions.
33 changes: 22 additions & 11 deletions drivers/usb/musb/musb_gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand All @@ -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)
Expand All @@ -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)
Expand Down
79 changes: 32 additions & 47 deletions drivers/usb/musb/musb_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -593,10 +593,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 */
Expand Down Expand Up @@ -693,29 +700,33 @@ 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
| MUSB_TXCSR_TXPKTRDY
);
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 {
Expand Down Expand Up @@ -759,34 +770,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(
Expand All @@ -803,25 +799,25 @@ 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

/* 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;

Expand Down Expand Up @@ -850,20 +846,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 */
Expand Down
59 changes: 36 additions & 23 deletions drivers/usb/musb/musbhsdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
musb_dma_completion(musb, musb_channel->epnum,
musb_channel->transmit);
Expand Down

0 comments on commit b6e434a

Please sign in to comment.