aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSergei Shtylyov <sshtylyov@ru.mvista.com>2009-03-26 21:26:40 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-04-17 13:50:25 -0400
commitc7bbc056a92476b3b3d70a8df7cc746ac5d56de7 (patch)
tree2d7254ae97e2af4068a5b2a81118d3b72c1550c8 /drivers/usb
parent78322c1a64387673f46afb8b5e31edec94e9603d (diff)
USB: musb: bugfixes for multi-packet TXDMA support
We really want to use DMA mode 1 for all multi-packet transfers; that's one IRQ on DMA completion, instead of one per packet. There is an important issue with such transfers, especially on the host side: when such transfers end with a full-size packet, we must defer musb_dma_completion() calls until the FIFO empties. Else we report URB completions too soon, and may clobber data in the FIFO fifo when writing the next packet (losing data). The Inventra DMA support uses DMA mode 1, but it ignores that issue. The CPPI DMA support uses mode 0, but doesn't handle its TXPKTRDY interrupts quite right either; it can get stale "packet ready" interrupts, and report transfer completion too early using slightly different code paths, also losing data. So I'm solving it in a generic way -- by adding a sort of the "interrupt filter" into musb_host_tx(), catching these cases where a DMA completion IRQ doesn't suffice and removing some needlessly controller-specific logic. When a TXDMA interrupt happens and DMA request mode 1 is active, that filter resets to mode 0 and defers URB completion processing until TXPKTRDY, unless the FIFO is already empty. Related filtering logic in Inventra and CPPI code gets removed. Since it should be competely safe now to use the DMA request mode 1 for host side transfers with the CPPI DMA controller, set it in musb_h_tx_dma_start() ... now renamed (and shared). [ dbrownell@users.sourceforge.net: don't introduce more CamElCase; use more concise explanations ] Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> Cc: Felipe Balbi <felipe.balbi@nokia.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/musb/cppi_dma.c22
-rw-r--r--drivers/usb/musb/musb_host.c68
-rw-r--r--drivers/usb/musb/musbhsdma.c7
3 files changed, 69 insertions, 28 deletions
diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
index 569ef0fed0f6..30e24891ed62 100644
--- a/drivers/usb/musb/cppi_dma.c
+++ b/drivers/usb/musb/cppi_dma.c
@@ -1228,27 +1228,7 @@ void cppi_completion(struct musb *musb, u32 rx, u32 tx)
1228 1228
1229 hw_ep = tx_ch->hw_ep; 1229 hw_ep = tx_ch->hw_ep;
1230 1230
1231 /* Peripheral role never repurposes the 1231 musb_dma_completion(musb, index + 1, 1);
1232 * endpoint, so immediate completion is
1233 * safe. Host role waits for the fifo
1234 * to empty (TXPKTRDY irq) before going
1235 * to the next queued bulk transfer.
1236 */
1237 if (is_host_active(cppi->musb)) {
1238#if 0
1239 /* WORKAROUND because we may
1240 * not always get TXKPTRDY ...
1241 */
1242 int csr;
1243
1244 csr = musb_readw(hw_ep->regs,
1245 MUSB_TXCSR);
1246 if (csr & MUSB_TXCSR_TXPKTRDY)
1247#endif
1248 completed = false;
1249 }
1250 if (completed)
1251 musb_dma_completion(musb, index + 1, 1);
1252 1232
1253 } else { 1233 } else {
1254 /* Bigger transfer than we could fit in 1234 /* Bigger transfer than we could fit in
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 521fd83f5b9d..518abfca1243 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -4,6 +4,7 @@
4 * Copyright 2005 Mentor Graphics Corporation 4 * Copyright 2005 Mentor Graphics Corporation
5 * Copyright (C) 2005-2006 by Texas Instruments 5 * Copyright (C) 2005-2006 by Texas Instruments
6 * Copyright (C) 2006-2007 Nokia Corporation 6 * Copyright (C) 2006-2007 Nokia Corporation
7 * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
7 * 8 *
8 * This program is free software; you can redistribute it and/or 9 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License 10 * modify it under the terms of the GNU General Public License
@@ -168,13 +169,15 @@ static inline void musb_h_tx_start(struct musb_hw_ep *ep)
168 169
169} 170}
170 171
171static inline void cppi_host_txdma_start(struct musb_hw_ep *ep) 172static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
172{ 173{
173 u16 txcsr; 174 u16 txcsr;
174 175
175 /* NOTE: no locks here; caller should lock and select EP */ 176 /* NOTE: no locks here; caller should lock and select EP */
176 txcsr = musb_readw(ep->regs, MUSB_TXCSR); 177 txcsr = musb_readw(ep->regs, MUSB_TXCSR);
177 txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS; 178 txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
179 if (is_cppi_enabled())
180 txcsr |= MUSB_TXCSR_DMAMODE;
178 musb_writew(ep->regs, MUSB_TXCSR, txcsr); 181 musb_writew(ep->regs, MUSB_TXCSR, txcsr);
179} 182}
180 183
@@ -279,7 +282,7 @@ start:
279 if (!hw_ep->tx_channel) 282 if (!hw_ep->tx_channel)
280 musb_h_tx_start(hw_ep); 283 musb_h_tx_start(hw_ep);
281 else if (is_cppi_enabled() || tusb_dma_omap()) 284 else if (is_cppi_enabled() || tusb_dma_omap())
282 cppi_host_txdma_start(hw_ep); 285 musb_h_tx_dma_start(hw_ep);
283 } 286 }
284} 287}
285 288
@@ -1250,6 +1253,67 @@ void musb_host_tx(struct musb *musb, u8 epnum)
1250 1253
1251 } 1254 }
1252 1255
1256 if (is_dma_capable() && dma && !status) {
1257 /*
1258 * DMA has completed. But if we're using DMA mode 1 (multi
1259 * packet DMA), we need a terminal TXPKTRDY interrupt before
1260 * we can consider this transfer completed, lest we trash
1261 * its last packet when writing the next URB's data. So we
1262 * switch back to mode 0 to get that interrupt; we'll come
1263 * back here once it happens.
1264 */
1265 if (tx_csr & MUSB_TXCSR_DMAMODE) {
1266 /*
1267 * We shouldn't clear DMAMODE with DMAENAB set; so
1268 * clear them in a safe order. That should be OK
1269 * once TXPKTRDY has been set (and I've never seen
1270 * it being 0 at this moment -- DMA interrupt latency
1271 * is significant) but if it hasn't been then we have
1272 * no choice but to stop being polite and ignore the
1273 * programmer's guide... :-)
1274 *
1275 * Note that we must write TXCSR with TXPKTRDY cleared
1276 * in order not to re-trigger the packet send (this bit
1277 * can't be cleared by CPU), and there's another caveat:
1278 * TXPKTRDY may be set shortly and then cleared in the
1279 * double-buffered FIFO mode, so we do an extra TXCSR
1280 * read for debouncing...
1281 */
1282 tx_csr &= musb_readw(epio, MUSB_TXCSR);
1283 if (tx_csr & MUSB_TXCSR_TXPKTRDY) {
1284 tx_csr &= ~(MUSB_TXCSR_DMAENAB |
1285 MUSB_TXCSR_TXPKTRDY);
1286 musb_writew(epio, MUSB_TXCSR,
1287 tx_csr | MUSB_TXCSR_H_WZC_BITS);
1288 }
1289 tx_csr &= ~(MUSB_TXCSR_DMAMODE |
1290 MUSB_TXCSR_TXPKTRDY);
1291 musb_writew(epio, MUSB_TXCSR,
1292 tx_csr | MUSB_TXCSR_H_WZC_BITS);
1293
1294 /*
1295 * There is no guarantee that we'll get an interrupt
1296 * after clearing DMAMODE as we might have done this
1297 * too late (after TXPKTRDY was cleared by controller).
1298 * Re-read TXCSR as we have spoiled its previous value.
1299 */
1300 tx_csr = musb_readw(epio, MUSB_TXCSR);
1301 }
1302
1303 /*
1304 * We may get here from a DMA completion or TXPKTRDY interrupt.
1305 * In any case, we must check the FIFO status here and bail out
1306 * only if the FIFO still has data -- that should prevent the
1307 * "missed" TXPKTRDY interrupts and deal with double-buffered
1308 * FIFO mode too...
1309 */
1310 if (tx_csr & (MUSB_TXCSR_FIFONOTEMPTY | MUSB_TXCSR_TXPKTRDY)) {
1311 DBG(2, "DMA complete but packet still in FIFO, "
1312 "CSR %04x\n", tx_csr);
1313 return;
1314 }
1315 }
1316
1253 /* REVISIT this looks wrong... */ 1317 /* REVISIT this looks wrong... */
1254 if (!status || dma || usb_pipeisoc(pipe)) { 1318 if (!status || dma || usb_pipeisoc(pipe)) {
1255 if (dma) 1319 if (dma)
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 8662e9e159c3..de0e24203673 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -304,12 +304,9 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
304 musb_channel->epnum, 304 musb_channel->epnum,
305 MUSB_TXCSR), 305 MUSB_TXCSR),
306 MUSB_TXCSR_TXPKTRDY); 306 MUSB_TXCSR_TXPKTRDY);
307 } else {
308 musb_dma_completion(
309 musb,
310 musb_channel->epnum,
311 musb_channel->transmit);
312 } 307 }
308 musb_dma_completion(musb, musb_channel->epnum,
309 musb_channel->transmit);
313 } 310 }
314 } 311 }
315 } 312 }