aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/at_xdmac.c
diff options
context:
space:
mode:
authorLudovic Desroches <ludovic.desroches@atmel.com>2016-05-12 10:54:09 -0400
committerVinod Koul <vinod.koul@intel.com>2016-05-30 01:17:52 -0400
commit53398f488821c2b5b15291e3debec6ad33f75d3d (patch)
tree3974eaea9bd86f6304484101647ac0ea5a3c409e /drivers/dma/at_xdmac.c
parent4a9723e8df68cfce4048517ee32e37f78854b6fb (diff)
dmaengine: at_xdmac: fix residue corruption
An unexpected value of CUBC can lead to a corrupted residue. A more complex sequence is needed to detect an inaccurate value for NCA or CUBC. Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com> Fixes: e1f7c9eee707 ("dmaengine: at_xdmac: creation of the atmel eXtended DMA Controller driver") Cc: stable@vger.kernel.org #v4.1 and later Reviewed-by: Nicolas Ferre <nicolas.ferre@atmel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/at_xdmac.c')
-rw-r--r--drivers/dma/at_xdmac.c54
1 files changed, 32 insertions, 22 deletions
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index ba9b0b73fa47..b02494e115fe 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -1400,6 +1400,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
1400 u32 cur_nda, check_nda, cur_ubc, mask, value; 1400 u32 cur_nda, check_nda, cur_ubc, mask, value;
1401 u8 dwidth = 0; 1401 u8 dwidth = 0;
1402 unsigned long flags; 1402 unsigned long flags;
1403 bool initd;
1403 1404
1404 ret = dma_cookie_status(chan, cookie, txstate); 1405 ret = dma_cookie_status(chan, cookie, txstate);
1405 if (ret == DMA_COMPLETE) 1406 if (ret == DMA_COMPLETE)
@@ -1435,34 +1436,43 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
1435 } 1436 }
1436 1437
1437 /* 1438 /*
1438 * When processing the residue, we need to read two registers but we 1439 * The easiest way to compute the residue should be to pause the DMA
1439 * can't do it in an atomic way. AT_XDMAC_CNDA is used to find where 1440 * but doing this can lead to miss some data as some devices don't
1440 * we stand in the descriptor list and AT_XDMAC_CUBC is used 1441 * have FIFO.
1441 * to know how many data are remaining for the current descriptor. 1442 * We need to read several registers because:
1442 * Since the dma channel is not paused to not loose data, between the 1443 * - DMA is running therefore a descriptor change is possible while
1443 * AT_XDMAC_CNDA and AT_XDMAC_CUBC read, we may have change of 1444 * reading these registers
1444 * descriptor. 1445 * - When the block transfer is done, the value of the CUBC register
1445 * For that reason, after reading AT_XDMAC_CUBC, we check if we are 1446 * is set to its initial value until the fetch of the next descriptor.
1446 * still using the same descriptor by reading a second time 1447 * This value will corrupt the residue calculation so we have to skip
1447 * AT_XDMAC_CNDA. If AT_XDMAC_CNDA has changed, it means we have to 1448 * it.
1448 * read again AT_XDMAC_CUBC. 1449 *
1450 * INITD -------- ------------
1451 * |____________________|
1452 * _______________________ _______________
1453 * NDA @desc2 \/ @desc3
1454 * _______________________/\_______________
1455 * __________ ___________ _______________
1456 * CUBC 0 \/ MAX desc1 \/ MAX desc2
1457 * __________/\___________/\_______________
1458 *
1459 * Since descriptors are aligned on 64 bits, we can assume that
1460 * the update of NDA and CUBC is atomic.
1449 * Memory barriers are used to ensure the read order of the registers. 1461 * Memory barriers are used to ensure the read order of the registers.
1450 * A max number of retries is set because unlikely it can never ends if 1462 * A max number of retries is set because unlikely it could never ends.
1451 * we are transferring a lot of data with small buffers.
1452 */ 1463 */
1453 cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
1454 rmb();
1455 cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC);
1456 for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) { 1464 for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) {
1457 rmb();
1458 check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; 1465 check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
1459 1466 rmb();
1460 if (likely(cur_nda == check_nda)) 1467 initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
1461 break;
1462
1463 cur_nda = check_nda;
1464 rmb(); 1468 rmb();
1465 cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC); 1469 cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC);
1470 rmb();
1471 cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
1472 rmb();
1473
1474 if ((check_nda == cur_nda) && initd)
1475 break;
1466 } 1476 }
1467 1477
1468 if (unlikely(retry >= AT_XDMAC_RESIDUE_MAX_RETRIES)) { 1478 if (unlikely(retry >= AT_XDMAC_RESIDUE_MAX_RETRIES)) {