mmc: am654_sdhci: Add the quirk to set TESTCD bit

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.ghidoli@toradex.com>
diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index 48fac7a..b4c60a4 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -105,6 +105,8 @@
 #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 {
@@ -350,10 +352,8 @@
 }
 
 #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;
 
@@ -368,12 +368,35 @@
 		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);
 
+	ret = sdhci_probe(dev);
+
-	return 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)
@@ -679,6 +702,9 @@
 
 	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;
 }
 
@@ -728,6 +754,8 @@
 
 	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)