From: Emanuele Ghidoli <emanuele.ghid...@toradex.com>

The ARASAN MMC controller on Keystone 3 class of devices need the SDCD
line to be connected for proper functioning.

In cases where this can't be connected, add a quirk to force the
controller into test mode and set the TESTCD bit. Use the flag
"ti,fails-without-test-cd", to implement this above quirk when required.

Additionally, this quirk also avoids waiting for the controller debounce
time.

This commit is similar to linux kernel commit c7666240ec76
("drivers: mmc: sdhci_am654: Add the quirk to set TESTCD bit").

Signed-off-by: Emanuele Ghidoli <emanuele.ghid...@toradex.com>
---
 drivers/mmc/am654_sdhci.c | 38 +++++++++++++++++++++++++++++++++-----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index fadab7d40bb7..43f890d269e9 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -106,6 +106,8 @@ struct am654_sdhci_plat {
 #define FREQSEL_2_BIT  BIT(2)
 #define STRBSEL_4_BIT  BIT(3)
 #define DLL_CALIB      BIT(4)
+       u32 quirks;
+#define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
 };
 
 struct timing_data {
@@ -351,10 +353,8 @@ int am654_sdhci_init(struct am654_sdhci_plat *plat)
 }
 
 #define MAX_SDCD_DEBOUNCE_TIME 2000
-static int am654_sdhci_deferred_probe(struct sdhci_host *host)
+static int am654_sdhci_cd_poll(struct mmc *mmc)
 {
-       struct udevice *dev = host->mmc->dev;
-       struct am654_sdhci_plat *plat = dev_get_plat(dev);
        unsigned long start;
        int val;
 
@@ -369,12 +369,35 @@ static int am654_sdhci_deferred_probe(struct sdhci_host 
*host)
                if (get_timer(start) > MAX_SDCD_DEBOUNCE_TIME)
                        return -ENOMEDIUM;
 
-               val = mmc_getcd(host->mmc);
+               val = mmc_getcd(mmc);
        } while (!val);
 
+       return 0;
+}
+
+static int am654_sdhci_deferred_probe(struct sdhci_host *host)
+{
+       struct udevice *dev = host->mmc->dev;
+       struct am654_sdhci_plat *plat = dev_get_plat(dev);
+       int ret;
+
+       if (!(plat->quirks & SDHCI_AM654_QUIRK_FORCE_CDTEST)) {
+               if (am654_sdhci_cd_poll(host->mmc))
+                       return -ENOMEDIUM;
+       }
+
        am654_sdhci_init(plat);
 
-       return sdhci_probe(dev);
+       ret = sdhci_probe(dev);
+
+       if (plat->quirks & SDHCI_AM654_QUIRK_FORCE_CDTEST) {
+               u8 hostctrlreg = sdhci_readb(host, SDHCI_HOST_CONTROL);
+
+               hostctrlreg |= SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST;
+               sdhci_writeb(host, hostctrlreg, SDHCI_HOST_CONTROL);
+       }
+
+       return ret;
 }
 
 static void am654_sdhci_write_b(struct sdhci_host *host, u8 val, int reg)
@@ -680,6 +703,9 @@ static int am654_sdhci_probe(struct udevice *dev)
 
        regmap_init_mem_index(dev_ofnode(dev), &plat->base, 1);
 
+       if (plat->quirks & SDHCI_AM654_QUIRK_FORCE_CDTEST)
+               am654_sdhci_deferred_probe(host);
+
        return 0;
 }
 
@@ -729,6 +755,8 @@ static int am654_sdhci_of_to_plat(struct udevice *dev)
 
        dev_read_u32(dev, "ti,strobe-sel", &plat->strb_sel);
        dev_read_u32(dev, "ti,clkbuf-sel", &plat->clkbuf_sel);
+       if (dev_read_bool(dev, "ti,fails-without-test-cd"))
+               plat->quirks |= SDHCI_AM654_QUIRK_FORCE_CDTEST;
 
        ret = mmc_of_parse(dev, cfg);
        if (ret)
-- 
2.34.1

Reply via email to