[V5,11/26] mmc: sdhci-uhs2: add reset function and uhs2_mode function

Message ID 20221014114601.15594-12-victor.shih@genesyslogic.com.tw
State New
Headers
Series Add support UHS-II for GL9755 |

Commit Message

Victor Shih Oct. 14, 2022, 11:45 a.m. UTC
  Sdhci_uhs2_reset() does a UHS-II specific reset operation.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Victor Shih <victor.shih@genesyslogic.com.tw>
---
 drivers/mmc/host/sdhci-pci-core.c |  1 +
 drivers/mmc/host/sdhci-pci-gli.c  |  1 +
 drivers/mmc/host/sdhci-uhs2.c     | 68 +++++++++++++++++++++++++++++++
 drivers/mmc/host/sdhci-uhs2.h     |  3 ++
 drivers/mmc/host/sdhci.c          |  3 +-
 drivers/mmc/host/sdhci.h          | 14 +++++++
 6 files changed, 89 insertions(+), 1 deletion(-)
  

Patch

diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 622b7de96c7f..a187379ad204 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -1926,6 +1926,7 @@  static const struct sdhci_ops sdhci_pci_ops = {
 	.reset		= sdhci_reset,
 	.set_uhs_signaling = sdhci_set_uhs_signaling,
 	.hw_reset		= sdhci_pci_hw_reset,
+	.uhs2_reset		= sdhci_uhs2_reset,
 };
 
 /*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index 4d509f656188..607cf69f45d0 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -1097,6 +1097,7 @@  static const struct sdhci_ops sdhci_gl9755_ops = {
 	.reset			= sdhci_reset,
 	.set_uhs_signaling	= sdhci_set_uhs_signaling,
 	.voltage_switch		= sdhci_gli_voltage_switch,
+	.uhs2_reset		= sdhci_uhs2_reset,
 };
 
 const struct sdhci_pci_fixes sdhci_gl9755 = {
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 08905ed081fb..0e82f98d1967 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -10,6 +10,7 @@ 
  *  Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>
 
 #include "sdhci.h"
@@ -49,6 +50,73 @@  void sdhci_uhs2_dump_regs(struct sdhci_host *host)
 }
 EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
 
+/*****************************************************************************\
+ *                                                                           *
+ * Low level functions                                                       *
+ *                                                                           *
+\*****************************************************************************/
+
+bool sdhci_uhs2_mode(struct sdhci_host *host)
+{
+	if ((host->mmc->caps2 & MMC_CAP2_SD_UHS2) &&
+	    (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+		(host->version >= SDHCI_SPEC_400) &&
+		(host->mmc->flags & MMC_UHS2_SUPPORT)))
+		return true;
+	else
+		return false;
+}
+
+/**
+ * sdhci_uhs2_reset - invoke SW reset
+ * @host: SDHCI host
+ * @mask: Control mask
+ *
+ * Invoke SW reset, depending on a bit in @mask and wait for completion.
+ */
+void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
+{
+	unsigned long timeout;
+	u32 val;
+
+	if (!(sdhci_uhs2_mode(host))) {
+		/**
+		 * u8  mask for legacy.
+		 * u16 mask for uhs-2.
+		 */
+		u8 u8_mask;
+
+		u8_mask = (mask & 0xFF);
+		sdhci_reset(host, u8_mask);
+
+		return;
+	}
+
+	sdhci_writew(host, mask, SDHCI_UHS2_SW_RESET);
+
+	if (mask & SDHCI_UHS2_SW_RESET_FULL) {
+		host->clock = 0;
+		/* Reset-all turns off SD Bus Power */
+		if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+			sdhci_runtime_pm_bus_off(host);
+	}
+
+	/* Wait max 100 ms */
+	timeout = 10000;
+
+	/* hw clears the bit when it's done */
+	if (read_poll_timeout_atomic(sdhci_readw, val, !(val & mask), 10,
+				     timeout, true, host, SDHCI_UHS2_SW_RESET)) {
+		pr_err("%s: %s: Reset 0x%x never completed.\n",
+					       __func__, mmc_hostname(host->mmc), (int)mask);
+		pr_err("%s: clean reset bit\n",
+					       mmc_hostname(host->mmc));
+		sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET);
+		return;
+	}
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);
+
 /*****************************************************************************\
  *                                                                           *
  * Driver init/exit                                                          *
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index afdb05d6056b..31776dcca5cf 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -11,6 +11,7 @@ 
 #define __SDHCI_UHS2_H
 
 #include <linux/bits.h>
+#include <linux/iopoll.h>
 
 /*
  * UHS-II Controller registers
@@ -210,5 +211,7 @@ 
 struct sdhci_host;
 
 void sdhci_uhs2_dump_regs(struct sdhci_host *host);
+bool sdhci_uhs2_mode(struct sdhci_host *host);
+void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
 
 #endif /* __SDHCI_UHS2_H */
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 4434838475bf..ab7ea55d9864 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -194,13 +194,14 @@  static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
 	pm_runtime_get_noresume(mmc_dev(host->mmc));
 }
 
-static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
+void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
 {
 	if (!host->bus_on)
 		return;
 	host->bus_on = false;
 	pm_runtime_put_noidle(mmc_dev(host->mmc));
 }
+EXPORT_SYMBOL_GPL(sdhci_runtime_pm_bus_off);
 
 void sdhci_reset(struct sdhci_host *host, u8 mask)
 {
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 64c2d7e78e29..3787ffe61c78 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -717,6 +717,19 @@  struct sdhci_ops {
 					     u8 power_mode);
 	unsigned int    (*get_ro)(struct sdhci_host *host);
 	void		(*reset)(struct sdhci_host *host, u8 mask);
+	/**
+	 * The sdhci_uhs2_reset callback is to implement for reset
+	 * @host: SDHCI host
+	 * @mask: Control mask
+	 *
+	 * Invoke reset, depending on a bit in @mask and wait for completion.
+	 * SD mode				UHS-II mode
+	 * SDHCI_RESET_ALL		SDHCI_UHS2_SW_RESET_FULL
+	 * SDHCI_RESET_CMD		SDHCI_RESET_CMD
+	 * SDHCI_RESET_DATA		SDHCI_UHS2_SW_RESET_SD
+	 *
+	 **/
+	void (*uhs2_reset)(struct sdhci_host *host, u16 mask);
 	int	(*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
 	void	(*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
 	void	(*hw_reset)(struct sdhci_host *host);
@@ -839,6 +852,7 @@  static inline void sdhci_read_caps(struct sdhci_host *host)
 	__sdhci_read_caps(host, NULL, NULL, NULL);
 }
 
+void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
 u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
 		   unsigned int *actual_clock);
 void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);