| From c5637476bbf9bb86c7f0413b8f4822a73d8d2d07 Mon Sep 17 00:00:00 2001 |
| From: Maxime Jayat <maxime.jayat@mobile-devices.fr> |
| Date: Thu, 22 Feb 2018 12:39:55 +0100 |
| Subject: dmaengine: at_xdmac: fix rare residue corruption |
| |
| From: Maxime Jayat <maxime.jayat@mobile-devices.fr> |
| |
| commit c5637476bbf9bb86c7f0413b8f4822a73d8d2d07 upstream. |
| |
| Despite the efforts made to correctly read the NDA and CUBC registers, |
| the order in which the registers are read could sometimes lead to an |
| inconsistent state. |
| |
| Re-using the timeline from the comments, this following timing of |
| registers reads could lead to reading NDA with value "@desc2" and |
| CUBC with value "MAX desc1": |
| |
| INITD -------- ------------ |
| |____________________| |
| _______________________ _______________ |
| NDA @desc2 \/ @desc3 |
| _______________________/\_______________ |
| __________ ___________ _______________ |
| CUBC 0 \/ MAX desc1 \/ MAX desc2 |
| __________/\___________/\_______________ |
| | | | | |
| Events:(1)(2) (3)(4) |
| |
| (1) check_nda = @desc2 |
| (2) initd = 1 |
| (3) cur_ubc = MAX desc1 |
| (4) cur_nda = @desc2 |
| |
| This is allowed by the condition ((check_nda == cur_nda) && initd), |
| despite cur_ubc and cur_nda being in the precise state we don't want. |
| |
| This error leads to incorrect residue computation. |
| |
| Fix it by inversing the order in which CUBC and INITD are read. This |
| makes sure that NDA and CUBC are always read together either _before_ |
| INITD goes to 0 or _after_ it is back at 1. |
| The case where NDA is read before INITD is at 0 and CUBC is read after |
| INITD is back at 1 will be rejected by check_nda and cur_nda being |
| different. |
| |
| Fixes: 53398f488821 ("dmaengine: at_xdmac: fix residue corruption") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Maxime Jayat <maxime.jayat@mobile-devices.fr> |
| Acked-by: Ludovic Desroches <ludovic.desroches@microchip.com> |
| Signed-off-by: Vinod Koul <vinod.koul@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/dma/at_xdmac.c | 4 ++-- |
| 1 file changed, 2 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/dma/at_xdmac.c |
| +++ b/drivers/dma/at_xdmac.c |
| @@ -1473,10 +1473,10 @@ at_xdmac_tx_status(struct dma_chan *chan |
| for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) { |
| check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; |
| rmb(); |
| - initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD); |
| - rmb(); |
| cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC); |
| rmb(); |
| + initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD); |
| + rmb(); |
| cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; |
| rmb(); |
| |