[PATCH] mmc: mtk-sd: report DMA stop timeouts as data errors

Pengpeng Hou pengpeng at iscas.ac.cn
Wed Jun 24 17:35:25 PDT 2026


msdc_data_xfer_done() stops the DMA engine before completing a data
request, but it only logs failures from the DMA stop and DMA-inactive
polls.

When the interrupt status also carries MSDC_INT_XFER_COMPL, the function
can still report the full data length as transferred even though the DMA
engine did not confirm that it stopped or became inactive.

Treat DMA stop/inactive timeouts as data errors.  This reuses the existing
reset/error path and prevents a timed out DMA stop from being reported as a
successful full-length transfer.

Signed-off-by: Pengpeng Hou <pengpeng at iscas.ac.cn>
---
 drivers/mmc/host/mtk-sd.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index b2680cc05..e3caa9c32 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -1583,7 +1583,7 @@ static void msdc_data_xfer_done(struct msdc_host *host, u32 events,
 	     | MSDC_INT_DMA_BDCSERR | MSDC_INT_DMA_GPDCSERR
 	     | MSDC_INT_DMA_PROTECT);
 	u32 val;
-	int ret;
+	int dma_err = 0, ret;
 
 	spin_lock_irqsave(&host->lock, flags);
 	done = !host->data;
@@ -1603,18 +1603,23 @@ static void msdc_data_xfer_done(struct msdc_host *host, u32 events,
 
 		ret = readl_poll_timeout_atomic(host->base + MSDC_DMA_CTRL, val,
 						!(val & MSDC_DMA_CTRL_STOP), 1, 20000);
-		if (ret)
+		if (ret) {
 			dev_dbg(host->dev, "DMA stop timed out\n");
+			dma_err = ret;
+		}
 
 		ret = readl_poll_timeout_atomic(host->base + MSDC_DMA_CFG, val,
 						!(val & MSDC_DMA_CFG_STS), 1, 20000);
-		if (ret)
+		if (ret) {
 			dev_dbg(host->dev, "DMA inactive timed out\n");
+			dma_err = ret;
+		}
 
 		sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask);
 		dev_dbg(host->dev, "DMA stop\n");
 
-		if ((events & MSDC_INT_XFER_COMPL) && (!stop || !stop->error)) {
+		if (!dma_err && (events & MSDC_INT_XFER_COMPL) &&
+		    (!stop || !stop->error)) {
 			data->bytes_xfered = data->blocks * data->blksz;
 		} else {
 			dev_dbg(host->dev, "interrupt events: %x\n", events);
@@ -1622,7 +1627,9 @@ static void msdc_data_xfer_done(struct msdc_host *host, u32 events,
 			host->error |= REQ_DAT_ERR;
 			data->bytes_xfered = 0;
 
-			if (events & MSDC_INT_DATTMO)
+			if (dma_err)
+				data->error = -ETIMEDOUT;
+			else if (events & MSDC_INT_DATTMO)
 				data->error = -ETIMEDOUT;
 			else if (events & MSDC_INT_DATCRCERR)
 				data->error = -EILSEQ;
-- 
2.50.1 (Apple Git-155)




More information about the linux-arm-kernel mailing list