mmc: dw_mmc: check fifo status with a timeout in fifo mode
authorHeiko Stuebner <heiko@sntech.de>
Fri, 21 Sep 2018 08:59:45 +0000 (10:59 +0200)
committerPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>
Tue, 2 Oct 2018 07:35:09 +0000 (09:35 +0200)
While trying to enable the dw_mmc on rk3188 I managed to confuse
and hang the dw_mmc controller into not delivering further data.
The fifo state never became ready and the driver was iterating in
the while loop reading 0-byte packets forever.

So inspired by how other implementations handle this, check the fifo-
state beforhand and add a timeout to catch any glaring fifo issues
without hanging uboot altogether.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Reviewed-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Acked-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
drivers/mmc/dw_mmc.c
include/dwmmc.h

index 13180fc0d69fa0bc178f8c29b877d18abedb3ce8..3c702b3ed815789d8dc42032df85c28da079ddf8 100644 (file)
@@ -92,6 +92,24 @@ static void dwmci_prepare_data(struct dwmci_host *host,
        dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks);
 }
 
+static int dwmci_fifo_ready(struct dwmci_host *host, u32 bit, u32 *len)
+{
+       u32 timeout = 20000;
+
+       *len = dwmci_readl(host, DWMCI_STATUS);
+       while (--timeout && (*len & bit)) {
+               udelay(200);
+               *len = dwmci_readl(host, DWMCI_STATUS);
+       }
+
+       if (!timeout) {
+               debug("%s: FIFO underflow timeout\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
 static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
 {
        int ret = 0;
@@ -122,7 +140,12 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
                        if (data->flags == MMC_DATA_READ &&
                            (mask & DWMCI_INTMSK_RXDR)) {
                                while (size) {
-                                       len = dwmci_readl(host, DWMCI_STATUS);
+                                       ret = dwmci_fifo_ready(host,
+                                                       DWMCI_FIFO_EMPTY,
+                                                       &len);
+                                       if (ret < 0)
+                                               break;
+
                                        len = (len >> DWMCI_FIFO_SHIFT) &
                                                    DWMCI_FIFO_MASK;
                                        len = min(size, len);
@@ -136,7 +159,12 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
                        } else if (data->flags == MMC_DATA_WRITE &&
                                   (mask & DWMCI_INTMSK_TXDR)) {
                                while (size) {
-                                       len = dwmci_readl(host, DWMCI_STATUS);
+                                       ret = dwmci_fifo_ready(host,
+                                                       DWMCI_FIFO_FULL,
+                                                       &len);
+                                       if (ret < 0)
+                                               break;
+
                                        len = fifo_depth - ((len >>
                                                   DWMCI_FIFO_SHIFT) &
                                                   DWMCI_FIFO_MASK);
index bc1d6e3abbceddef5a1826d051ccafd8748e8fda..0f9d51b557911ce60acca19ed0d356049c438117 100644 (file)
 #define DWMCI_CTYPE_8BIT       (1 << 16)
 
 /* Status Register */
+#define DWMCI_FIFO_EMPTY       (1 << 2)
+#define DWMCI_FIFO_FULL                (1 << 3)
 #define DWMCI_BUSY             (1 << 9)
 #define DWMCI_FIFO_MASK                0x1fff
 #define DWMCI_FIFO_SHIFT       17