x86: sandbox: pch: Add a CONFIG option for PCH
[oweals/u-boot.git] / drivers / mmc / dw_mmc.c
index 700f7644329bc7e72019c315ce5699a799f17edd..93a836eac3629bde23f89e62747e17c41eea3ab0 100644 (file)
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2012 SAMSUNG Electronics
  * Jaehoon Chung <jh80.chung@samsung.com>
  * Rajeshawari Shinde <rajeshwari.s@samsung.com>
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <bouncebuf.h>
@@ -13,6 +12,7 @@
 #include <memalign.h>
 #include <mmc.h>
 #include <dwmmc.h>
+#include <wait_bit.h>
 
 #define PAGE_SIZE 4096
 
@@ -56,6 +56,9 @@ static void dwmci_prepare_data(struct dwmci_host *host,
 
        dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
 
+       /* Clear IDMAC interrupt */
+       dwmci_writel(host, DWMCI_IDSTS, 0xFFFFFFFF);
+
        data_start = (ulong)cur_idmac;
        dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac);
 
@@ -93,6 +96,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;
@@ -123,7 +144,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);
@@ -137,7 +163,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);
@@ -184,7 +215,7 @@ static int dwmci_set_transfer_mode(struct dwmci_host *host,
        return mode;
 }
 
-#ifdef CONFIG_DM_MMC_OPS
+#ifdef CONFIG_DM_MMC
 static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
                   struct mmc_data *data)
 {
@@ -290,6 +321,10 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
        } else if (mask & DWMCI_INTMSK_RE) {
                debug("%s: Response Error.\n", __func__);
                return -EIO;
+       } else if ((cmd->resp_type & MMC_RSP_CRC) &&
+                  (mask & DWMCI_INTMSK_RCRC)) {
+               debug("%s: Response CRC Error.\n", __func__);
+               return -EIO;
        }
 
 
@@ -309,6 +344,18 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 
                /* only dma mode need it */
                if (!host->fifo_mode) {
+                       if (data->flags == MMC_DATA_READ)
+                               mask = DWMCI_IDINTEN_RI;
+                       else
+                               mask = DWMCI_IDINTEN_TI;
+                       ret = wait_for_bit_le32(host->ioaddr + DWMCI_IDSTS,
+                                               mask, true, 1000, false);
+                       if (ret)
+                               debug("%s: DWMCI_IDINTEN mask 0x%x timeout.\n",
+                                     __func__, mask);
+                       /* clear interrupts */
+                       dwmci_writel(host, DWMCI_IDSTS, DWMCI_IDINTEN_MASK);
+
                        ctrl = dwmci_readl(host, DWMCI_CTRL);
                        ctrl &= ~(DWMCI_DMA_EN);
                        dwmci_writel(host, DWMCI_CTRL, ctrl);
@@ -383,7 +430,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
        return 0;
 }
 
-#ifdef CONFIG_DM_MMC_OPS
+#ifdef CONFIG_DM_MMC
 static int dwmci_set_ios(struct udevice *dev)
 {
        struct mmc *mmc = mmc_get_mmc_dev(dev);
@@ -463,10 +510,13 @@ static int dwmci_init(struct mmc *mmc)
        dwmci_writel(host, DWMCI_CLKENA, 0);
        dwmci_writel(host, DWMCI_CLKSRC, 0);
 
+       if (!host->fifo_mode)
+               dwmci_writel(host, DWMCI_IDINTEN, DWMCI_IDINTEN_MASK);
+
        return 0;
 }
 
-#ifdef CONFIG_DM_MMC_OPS
+#ifdef CONFIG_DM_MMC
 int dwmci_probe(struct udevice *dev)
 {
        struct mmc *mmc = mmc_get_mmc_dev(dev);
@@ -491,7 +541,7 @@ void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host,
                u32 max_clk, u32 min_clk)
 {
        cfg->name = host->name;
-#ifndef CONFIG_DM_MMC_OPS
+#ifndef CONFIG_DM_MMC
        cfg->ops = &dwmci_ops;
 #endif
        cfg->f_min = min_clk;