summaryrefslogtreecommitdiff
path: root/drivers/mmc/dw_mmc.c
diff options
context:
space:
mode:
authorHeiko Stuebner <heiko@sntech.de>2018-09-21 11:59:45 +0300
committerPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>2018-10-02 10:35:09 +0300
commit05fa06b9606387ad4a65723d93a83b36a98d600b (patch)
tree47003edf45f9519a4d6026b51f5c45290a33f19a /drivers/mmc/dw_mmc.c
parent2ba8bf207481cfb319f54a1bef67f6f068831a58 (diff)
downloadu-boot-05fa06b9606387ad4a65723d93a83b36a98d600b.tar.xz
mmc: dw_mmc: check fifo status with a timeout in fifo mode
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>
Diffstat (limited to 'drivers/mmc/dw_mmc.c')
-rw-r--r--drivers/mmc/dw_mmc.c32
1 files changed, 30 insertions, 2 deletions
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 13180fc0d6..3c702b3ed8 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -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);