mmc: mediatek: wait dma stop bit reset to 0
authorMengqi Zhang <mengqi.zhang@mediatek.com>
Thu, 9 Jun 2022 11:22:39 +0000 (19:22 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jun 2022 07:03:19 +0000 (09:03 +0200)
commit 89bcd9a64b849380ef57e3032b307574e48db524 upstream.

MediaTek IP requires that after dma stop, it need to wait this dma stop
bit auto-reset to 0. When bus is in high loading state, it will take a
while for the dma stop complete. If there is no waiting operation here,
when program runs to clear fifo and reset, bus will hang.

In addition, there should be no return in msdc_data_xfer_next() if
there is data need be transferred, because no matter what error occurs
here, it should continue to excute to the following mmc_request_done.
Otherwise the core layer may wait complete forever.

Signed-off-by: Mengqi Zhang <mengqi.zhang@mediatek.com>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20220609112239.18911-1-mengqi.zhang@mediatek.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/mmc/host/mtk-sd.c

index 1ac92015992ed157456e3e4ebafa9d251ea5d687..f9b2897569bb49162c5c39087abca3a2fd91cdb0 100644 (file)
@@ -1355,7 +1355,7 @@ static void msdc_data_xfer_next(struct msdc_host *host, struct mmc_request *mrq)
                msdc_request_done(host, mrq);
 }
 
-static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
+static void msdc_data_xfer_done(struct msdc_host *host, u32 events,
                                struct mmc_request *mrq, struct mmc_data *data)
 {
        struct mmc_command *stop;
@@ -1375,7 +1375,7 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
        spin_unlock_irqrestore(&host->lock, flags);
 
        if (done)
-               return true;
+               return;
        stop = data->stop;
 
        if (check_data || (stop && stop->error)) {
@@ -1384,12 +1384,15 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
                sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP,
                                1);
 
+               ret = readl_poll_timeout_atomic(host->base + MSDC_DMA_CTRL, val,
+                                               !(val & MSDC_DMA_CTRL_STOP), 1, 20000);
+               if (ret)
+                       dev_dbg(host->dev, "DMA stop timed out\n");
+
                ret = readl_poll_timeout_atomic(host->base + MSDC_DMA_CFG, val,
                                                !(val & MSDC_DMA_CFG_STS), 1, 20000);
-               if (ret) {
-                       dev_dbg(host->dev, "DMA stop timed out\n");
-                       return false;
-               }
+               if (ret)
+                       dev_dbg(host->dev, "DMA inactive timed out\n");
 
                sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask);
                dev_dbg(host->dev, "DMA stop\n");
@@ -1414,9 +1417,7 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
                }
 
                msdc_data_xfer_next(host, mrq);
-               done = true;
        }
-       return done;
 }
 
 static void msdc_set_buswidth(struct msdc_host *host, u32 width)
@@ -2347,6 +2348,9 @@ static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
        if (recovery) {
                sdr_set_field(host->base + MSDC_DMA_CTRL,
                              MSDC_DMA_CTRL_STOP, 1);
+               if (WARN_ON(readl_poll_timeout(host->base + MSDC_DMA_CTRL, val,
+                       !(val & MSDC_DMA_CTRL_STOP), 1, 3000)))
+                       return;
                if (WARN_ON(readl_poll_timeout(host->base + MSDC_DMA_CFG, val,
                        !(val & MSDC_DMA_CFG_STS), 1, 3000)))
                        return;