diff options
author | Ludovic Desroches <ludovic.desroches@atmel.com> | 2016-05-12 10:54:09 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2016-05-30 01:17:52 -0400 |
commit | 53398f488821c2b5b15291e3debec6ad33f75d3d (patch) | |
tree | 3974eaea9bd86f6304484101647ac0ea5a3c409e /drivers/dma/at_xdmac.c | |
parent | 4a9723e8df68cfce4048517ee32e37f78854b6fb (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.c | 54 |
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)) { |