The commit 0252924ac6d4 ("mmc: dw_mmc: Extract FIFO data transfer into a
separate routine") unintentionally changed behavior of the FIFO data
transfer routine.

When data is read and size reaches 0 the original loop would wait on
DWMCI_INTMSK_DTO or timeout. The remaining size to read is no longer
tracked across dwmci_data_transfer_fifo() calls and because of this an
extra call to fifo() and dwmci_fifo_ready() may now trigger a FIFO
underflow timeout.

  Buswidth = 4, clock: 50000000
  Sending CMD16
  Sending CMD17
  dwmci_fifo_ready: FIFO underflow timeout
  Sending CMD16
  Sending CMD18
  dwmci_fifo_ready: FIFO underflow timeout
  Sending CMD12
  ## Checking hash(es) for config config-1 ... OK

Restore old behavior and track remaining size to read across calls to
fix this.

Fixes: 0252924ac6d4 ("mmc: dw_mmc: Extract FIFO data transfer into a separate 
routine")
Signed-off-by: Jonas Karlman <[email protected]>
---
Reading FIT from SD-card take ~20-30 seconds on a Rockchip RK3328 board
without this fixed, and goes back to sub second once fixed.
---
 drivers/mmc/dw_mmc.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 8551eac70185..98b85fd1795a 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -214,16 +214,15 @@ static unsigned int dwmci_get_timeout(struct mmc *mmc, 
const unsigned int size)
        return timeout;
 }
 
-static int dwmci_data_transfer_fifo(struct dwmci_host *host,
-                                   struct mmc_data *data, u32 mask)
+static u32 dwmci_data_transfer_fifo(struct dwmci_host *host,
+                                   struct mmc_data *data, u32 size, u32 mask)
 {
        const u32 int_rx = mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO);
        const u32 int_tx = mask & DWMCI_INTMSK_TXDR;
        int ret = 0;
-       u32 len = 0, size, i;
+       u32 len = 0, i;
        u32 *buf;
 
-       size = (data->blocksize * data->blocks) / 4;
        if (!host->fifo_mode || !size)
                return 0;
 
@@ -261,7 +260,7 @@ static int dwmci_data_transfer_fifo(struct dwmci_host *host,
                dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_TXDR);
        }
 
-       return ret;
+       return size;
 }
 
 static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
@@ -273,6 +272,7 @@ static int dwmci_data_transfer(struct dwmci_host *host, 
struct mmc_data *data)
 
        size = data->blocksize * data->blocks;
        timeout = dwmci_get_timeout(mmc, size);
+       size /= 4;
 
        for (;;) {
                mask = dwmci_readl(host, DWMCI_RINTSTS);
@@ -283,7 +283,7 @@ static int dwmci_data_transfer(struct dwmci_host *host, 
struct mmc_data *data)
                        break;
                }
 
-               ret = dwmci_data_transfer_fifo(host, data, mask);
+               size = dwmci_data_transfer_fifo(host, data, size, mask);
 
                /* Data arrived correctly */
                if (mask & DWMCI_INTMSK_DTO) {
-- 
2.46.2

Reply via email to