From 760de36744213a6bdfb84c7b7a51e3eea2e83d6c Mon Sep 17 00:00:00 2001
From: Eugen Hristev <eugen.hristev@microchip.com>
Date: Wed, 1 Jul 2020 16:43:01 +0300
Subject: [PATCH 087/110] mmc: sdhci-of-at91: add support for HS400 and HS400ES

Add support for HS400 and HS400ES timings.

Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
(cherry picked from linux-6.1-trunk/at91/mmc)
Signed-off-by: Andrei Simion <andrei.simion@microchip.com>
---
 drivers/mmc/host/sdhci-of-at91.c | 45 +++++++++++++++++++++++++++-----
 1 file changed, 39 insertions(+), 6 deletions(-)

--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -27,6 +27,9 @@
 #define		SDMMC_MC1R_DDR		BIT(3)
 #define		SDMMC_MC1R_RSTN		BIT(6)
 #define		SDMMC_MC1R_FCD		BIT(7)
+#define SDMMC_MC3R	0x206
+#define		SDMMC_MC3R_HS400EN	BIT(0)
+#define		SDMMC_MC3R_ESMEN	BIT(1)
 #define SDMMC_CACR	0x230
 #define		SDMMC_CACR_CAPWREN	BIT(0)
 #define		SDMMC_CACR_KEY		(0x46 << 8)
@@ -103,18 +106,32 @@ static void sdhci_at91_set_clock(struct
 static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host,
 					 unsigned int timing)
 {
-	u8 mc1r;
-	u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	u16 clk;
+	u8 mc3r, mc1r;
+
+	mc1r = readb(host->ioaddr + SDMMC_MC1R);
+	mc3r = readb(host->ioaddr + SDMMC_MC3R);
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
 	/* SDCLK must be disabled while changing the mode */
 	if (clk & SDHCI_CLOCK_CARD_EN)
 		sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN,
 			     SDHCI_CLOCK_CONTROL);
 
-	if (timing == MMC_TIMING_MMC_DDR52) {
-		mc1r = sdhci_readb(host, SDMMC_MC1R);
+	if (timing == MMC_TIMING_MMC_DDR52 || timing == MMC_TIMING_MMC_HS400)
 		mc1r |= SDMMC_MC1R_DDR;
-		sdhci_writeb(host, mc1r, SDMMC_MC1R);
-	}
+	else
+		mc1r &= ~SDMMC_MC1R_DDR;
+
+	sdhci_writeb(host, mc1r, SDMMC_MC1R);
+
+	if (timing == MMC_TIMING_MMC_HS400)
+		mc3r |= SDMMC_MC3R_HS400EN;
+	else
+		mc3r &= ~SDMMC_MC3R_HS400EN;
+
+	writeb(mc3r, host->ioaddr + SDMMC_MC3R);
+
 	sdhci_set_uhs_signaling(host, timing);
 
 	/* reenable SDCLK */
@@ -360,6 +377,20 @@ static const struct dev_pm_ops sdhci_at9
 	RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, sdhci_at91_runtime_resume, NULL)
 };
 
+static void at91_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	u8 mc3r;
+
+	mc3r = readb(host->ioaddr + SDMMC_MC3R);
+	if (ios->enhanced_strobe)
+		mc3r |= SDMMC_MC3R_ESMEN;
+	else
+		mc3r &= ~SDMMC_MC3R_ESMEN;
+
+	writeb(mc3r, host->ioaddr + SDMMC_MC3R);
+}
+
 static int sdhci_at91_probe(struct platform_device *pdev)
 {
 	const struct sdhci_at91_soc_data	*soc_data;
@@ -433,6 +464,8 @@ static int sdhci_at91_probe(struct platf
 	if (ret)
 		goto pm_runtime_disable;
 
+	host->mmc_host_ops.hs400_enhanced_strobe = at91_sdhci_hs400_enhanced_strobe;
+
 	/*
 	 * When calling sdhci_runtime_suspend_host(), the sdhci layer makes
 	 * the assumption that all the clocks of the controller are disabled.
