diff options
author | Sergei Shtylyov <sshtylyov@ru.mvista.com> | 2009-03-26 21:27:47 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-04-17 13:50:25 -0400 |
commit | b6e434a5404b9ce8c285ea081b6ea5c523b29db4 (patch) | |
tree | 8a1d0aac0692859aeb97931b86bf60314b663567 /drivers/usb/musb/musbhsdma.c | |
parent | c7bbc056a92476b3b3d70a8df7cc746ac5d56de7 (diff) |
USB: 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@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>
Diffstat (limited to 'drivers/usb/musb/musbhsdma.c')
-rw-r--r-- | drivers/usb/musb/musbhsdma.c | 59 |
1 files changed, 36 insertions, 23 deletions
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index de0e24203673..5e83f96d6b77 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) | |||
195 | void __iomem *mbase = musb_channel->controller->base; | 195 | void __iomem *mbase = musb_channel->controller->base; |
196 | 196 | ||
197 | u8 bchannel = musb_channel->idx; | 197 | u8 bchannel = musb_channel->idx; |
198 | int offset; | ||
198 | u16 csr; | 199 | u16 csr; |
199 | 200 | ||
200 | if (channel->status == MUSB_DMA_STATUS_BUSY) { | 201 | if (channel->status == MUSB_DMA_STATUS_BUSY) { |
201 | if (musb_channel->transmit) { | 202 | if (musb_channel->transmit) { |
202 | 203 | offset = MUSB_EP_OFFSET(musb_channel->epnum, | |
203 | csr = musb_readw(mbase, | 204 | MUSB_TXCSR); |
204 | MUSB_EP_OFFSET(musb_channel->epnum, | 205 | |
205 | MUSB_TXCSR)); | 206 | /* |
206 | csr &= ~(MUSB_TXCSR_AUTOSET | | 207 | * The programming guide says that we must clear |
207 | MUSB_TXCSR_DMAENAB | | 208 | * the DMAENAB bit before the DMAMODE bit... |
208 | MUSB_TXCSR_DMAMODE); | 209 | */ |
209 | musb_writew(mbase, | 210 | csr = musb_readw(mbase, offset); |
210 | MUSB_EP_OFFSET(musb_channel->epnum, MUSB_TXCSR), | 211 | csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB); |
211 | csr); | 212 | musb_writew(mbase, offset, csr); |
213 | csr &= ~MUSB_TXCSR_DMAMODE; | ||
214 | musb_writew(mbase, offset, csr); | ||
212 | } else { | 215 | } else { |
213 | csr = musb_readw(mbase, | 216 | offset = MUSB_EP_OFFSET(musb_channel->epnum, |
214 | MUSB_EP_OFFSET(musb_channel->epnum, | 217 | MUSB_RXCSR); |
215 | MUSB_RXCSR)); | 218 | |
219 | csr = musb_readw(mbase, offset); | ||
216 | csr &= ~(MUSB_RXCSR_AUTOCLEAR | | 220 | csr &= ~(MUSB_RXCSR_AUTOCLEAR | |
217 | MUSB_RXCSR_DMAENAB | | 221 | MUSB_RXCSR_DMAENAB | |
218 | MUSB_RXCSR_DMAMODE); | 222 | MUSB_RXCSR_DMAMODE); |
219 | musb_writew(mbase, | 223 | musb_writew(mbase, offset, csr); |
220 | MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR), | ||
221 | csr); | ||
222 | } | 224 | } |
223 | 225 | ||
224 | musb_writew(mbase, | 226 | musb_writew(mbase, |
@@ -296,14 +298,25 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) | |||
296 | && ((channel->desired_mode == 0) | 298 | && ((channel->desired_mode == 0) |
297 | || (channel->actual_len & | 299 | || (channel->actual_len & |
298 | (musb_channel->max_packet_sz - 1))) | 300 | (musb_channel->max_packet_sz - 1))) |
299 | ) { | 301 | ) { |
302 | u8 epnum = musb_channel->epnum; | ||
303 | int offset = MUSB_EP_OFFSET(epnum, | ||
304 | MUSB_TXCSR); | ||
305 | u16 txcsr; | ||
306 | |||
307 | /* | ||
308 | * The programming guide says that we | ||
309 | * must clear DMAENAB before DMAMODE. | ||
310 | */ | ||
311 | musb_ep_select(mbase, epnum); | ||
312 | txcsr = musb_readw(mbase, offset); | ||
313 | txcsr &= ~(MUSB_TXCSR_DMAENAB | ||
314 | | MUSB_TXCSR_AUTOSET); | ||
315 | musb_writew(mbase, offset, txcsr); | ||
300 | /* Send out the packet */ | 316 | /* Send out the packet */ |
301 | musb_ep_select(mbase, | 317 | txcsr &= ~MUSB_TXCSR_DMAMODE; |
302 | musb_channel->epnum); | 318 | txcsr |= MUSB_TXCSR_TXPKTRDY; |
303 | musb_writew(mbase, MUSB_EP_OFFSET( | 319 | musb_writew(mbase, offset, txcsr); |
304 | musb_channel->epnum, | ||
305 | MUSB_TXCSR), | ||
306 | MUSB_TXCSR_TXPKTRDY); | ||
307 | } | 320 | } |
308 | musb_dma_completion(musb, musb_channel->epnum, | 321 | musb_dma_completion(musb, musb_channel->epnum, |
309 | musb_channel->transmit); | 322 | musb_channel->transmit); |