OSDN Git Service

mmc: sdhci-esdhc-imx: add CMDQ support
authorBOUGH CHEN <haibo.chen@nxp.com>
Mon, 7 Jan 2019 10:11:39 +0000 (10:11 +0000)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 25 Feb 2019 07:40:58 +0000 (08:40 +0100)
Add CMDQ support for imx8qm/imx8qxp.

Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
[Ulf: Rebased on top of latest changes]
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/Kconfig
drivers/mmc/host/sdhci-esdhc-imx.c

index a44ec8b..d1769f1 100644 (file)
@@ -224,6 +224,7 @@ config MMC_SDHCI_ESDHC_IMX
        depends on ARCH_MXC
        depends on MMC_SDHCI_PLTFM
        select MMC_SDHCI_IO_ACCESSORS
+       select MMC_CQHCI
        help
          This selects the Freescale eSDHC/uSDHC controller support
          found on i.MX25, i.MX35 i.MX5x and i.MX6x.
index 52a9382..9eb6562 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/pm_runtime.h>
 #include "sdhci-pltfm.h"
 #include "sdhci-esdhc.h"
+#include "cqhci.h"
 
 #define ESDHC_SYS_CTRL_DTOCV_MASK      0x0f
 #define        ESDHC_CTRL_D3CD                 0x08
  */
 #define ESDHC_INT_VENDOR_SPEC_DMA_ERR  (1 << 28)
 
+/* the address offset of CQHCI */
+#define ESDHC_CQHCI_ADDR_OFFSET                0x100
+
 /*
  * The CMDTYPE of the CMD register (offset 0xE) should be set to
  * "11" when the STOP CMD12 is issued on imx53 to abort one
 #define ESDHC_FLAG_ERR010450           BIT(10)
 /* The IP supports HS400ES mode */
 #define ESDHC_FLAG_HS400_ES            BIT(11)
+/* The IP has Host Controller Interface for Command Queuing */
+#define ESDHC_FLAG_CQHCI               BIT(12)
 
 struct esdhc_soc_data {
        u32 flags;
@@ -198,7 +204,8 @@ static const struct esdhc_soc_data usdhc_imx7d_data = {
 static struct esdhc_soc_data usdhc_imx8qxp_data = {
        .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
                        | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
-                       | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES,
+                       | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
+                       | ESDHC_FLAG_CQHCI,
 };
 
 struct pltfm_imx_data {
@@ -1094,6 +1101,19 @@ static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
                        SDHCI_TIMEOUT_CONTROL);
 }
 
+static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
+{
+       int cmd_error = 0;
+       int data_error = 0;
+
+       if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
+               return intmask;
+
+       cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+
+       return 0;
+}
+
 static struct sdhci_ops sdhci_esdhc_ops = {
        .read_l = esdhc_readl_le,
        .read_w = esdhc_readw_le,
@@ -1110,6 +1130,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
        .set_bus_width = esdhc_pltfm_set_bus_width,
        .set_uhs_signaling = esdhc_set_uhs_signaling,
        .reset = esdhc_reset,
+       .irq = esdhc_cqhci_irq,
 };
 
 static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@@ -1185,6 +1206,55 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
        }
 }
 
+static void esdhc_cqe_enable(struct mmc_host *mmc)
+{
+       struct sdhci_host *host = mmc_priv(mmc);
+       u32 reg;
+       u16 mode;
+       int count = 10;
+
+       /*
+        * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
+        * the case after tuning, so ensure the buffer is drained.
+        */
+       reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+       while (reg & SDHCI_DATA_AVAILABLE) {
+               sdhci_readl(host, SDHCI_BUFFER);
+               reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+               if (count-- == 0) {
+                       dev_warn(mmc_dev(host->mmc),
+                               "CQE may get stuck because the Buffer Read Enable bit is set\n");
+                       break;
+               }
+               mdelay(1);
+       }
+
+       /*
+        * Runtime resume will reset the entire host controller, which
+        * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
+        * Here set DMAEN and BCEN when enable CMDQ.
+        */
+       mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
+       if (host->flags & SDHCI_REQ_USE_DMA)
+               mode |= SDHCI_TRNS_DMA;
+       if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
+               mode |= SDHCI_TRNS_BLK_CNT_EN;
+       sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
+
+       sdhci_cqe_enable(mmc);
+}
+
+static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
+{
+       sdhci_dumpregs(mmc_priv(mmc));
+}
+
+static const struct cqhci_host_ops esdhc_cqhci_ops = {
+       .enable         = esdhc_cqe_enable,
+       .disable        = sdhci_cqe_disable,
+       .dumpregs       = esdhc_sdhci_dumpregs,
+};
+
 #ifdef CONFIG_OF
 static int
 sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
@@ -1316,6 +1386,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                        of_match_device(imx_esdhc_dt_ids, &pdev->dev);
        struct sdhci_pltfm_host *pltfm_host;
        struct sdhci_host *host;
+       struct cqhci_host *cq_host;
        int err;
        struct pltfm_imx_data *imx_data;
 
@@ -1406,6 +1477,22 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                                        esdhc_hs400_enhanced_strobe;
        }
 
+       if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
+               host->mmc->caps2 |= MMC_CAP2_CQE;
+               cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
+               if (IS_ERR(cq_host)) {
+                       err = PTR_ERR(cq_host);
+                       goto disable_ahb_clk;
+               }
+
+               cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
+               cq_host->ops = &esdhc_cqhci_ops;
+
+               err = cqhci_init(cq_host, host->mmc, false);
+               if (err)
+                       goto disable_ahb_clk;
+       }
+
        if (of_id)
                err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
        else
@@ -1466,6 +1553,13 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
 static int sdhci_esdhc_suspend(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
+       int ret;
+
+       if (host->mmc->caps2 & MMC_CAP2_CQE) {
+               ret = cqhci_suspend(host->mmc);
+               if (ret)
+                       return ret;
+       }
 
        if (host->tuning_mode != SDHCI_TUNING_MODE_3)
                mmc_retune_needed(host->mmc);
@@ -1476,11 +1570,19 @@ static int sdhci_esdhc_suspend(struct device *dev)
 static int sdhci_esdhc_resume(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
+       int ret;
 
        /* re-initialize hw state in case it's lost in low power mode */
        sdhci_esdhc_imx_hwinit(host);
 
-       return sdhci_resume_host(host);
+       ret = sdhci_resume_host(host);
+       if (ret)
+               return ret;
+
+       if (host->mmc->caps2 & MMC_CAP2_CQE)
+               ret = cqhci_resume(host->mmc);
+
+       return ret;
 }
 #endif
 
@@ -1492,6 +1594,12 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev)
        struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
        int ret;
 
+       if (host->mmc->caps2 & MMC_CAP2_CQE) {
+               ret = cqhci_suspend(host->mmc);
+               if (ret)
+                       return ret;
+       }
+
        ret = sdhci_runtime_suspend_host(host);
        if (ret)
                return ret;
@@ -1535,7 +1643,10 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
        if (err)
                goto disable_ipg_clk;
 
-       return 0;
+       if (host->mmc->caps2 & MMC_CAP2_CQE)
+               err = cqhci_resume(host->mmc);
+
+       return err;
 
 disable_ipg_clk:
        if (!sdhci_sdio_irq_enabled(host))