mmc: sdhci-uhs2: add add_host() and others to set up the driver
authorVictor Shih <victor.shih@genesyslogic.com.tw>
Fri, 18 Oct 2024 10:53:25 +0000 (18:53 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Thu, 24 Oct 2024 12:37:19 +0000 (14:37 +0200)
This is a UHS-II version of sdhci's add_host/remove_host operation.
Any sdhci drivers which are capable of handling UHS-II cards must
call those functions instead of the corresponding sdhci's.

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>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Message-ID: <20241018105333.4569-9-victorshihgli@gmail.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci-uhs2.c
drivers/mmc/host/sdhci-uhs2.h

index 6b249eb..d3af620 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/iopoll.h>
 #include <linux/bitfield.h>
+#include <linux/regulator/consumer.h>
 
 #include "sdhci.h"
 #include "sdhci-uhs2.h"
@@ -224,6 +225,96 @@ static void __exit sdhci_uhs2_mod_exit(void)
 }
 module_exit(sdhci_uhs2_mod_exit);
 
+/*****************************************************************************\
+ *
+ * Device allocation/registration                                            *
+ *                                                                           *
+\*****************************************************************************/
+
+static void __sdhci_uhs2_add_host_v4(struct sdhci_host *host, u32 caps1)
+{
+       struct mmc_host *mmc;
+       u32 max_current_caps2;
+
+       mmc = host->mmc;
+
+       /* Support UHS2 */
+       if (caps1 & SDHCI_SUPPORT_UHS2)
+               mmc->caps2 |= MMC_CAP2_SD_UHS2;
+
+       max_current_caps2 = sdhci_readl(host, SDHCI_MAX_CURRENT_1);
+
+       if ((caps1 & SDHCI_CAN_VDD2_180) &&
+           !max_current_caps2 &&
+           !IS_ERR(mmc->supply.vqmmc2)) {
+               /* UHS2 - VDD2 */
+               int curr = regulator_get_current_limit(mmc->supply.vqmmc2);
+
+               if (curr > 0) {
+                       /* convert to SDHCI_MAX_CURRENT format */
+                       curr = curr / 1000;  /* convert to mA */
+                       curr = curr / SDHCI_MAX_CURRENT_MULTIPLIER;
+                       curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
+                       max_current_caps2 = curr;
+               }
+       }
+
+       if (!(caps1 & SDHCI_CAN_VDD2_180))
+               mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
+}
+
+static void __sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
+{
+       if (!mmc_card_uhs2(host->mmc))
+               return;
+
+       if (!dead)
+               sdhci_uhs2_reset(host, SDHCI_UHS2_SW_RESET_FULL);
+}
+
+int sdhci_uhs2_add_host(struct sdhci_host *host)
+{
+       struct mmc_host *mmc = host->mmc;
+       int ret;
+
+       ret = sdhci_setup_host(host);
+       if (ret)
+               return ret;
+
+       if (host->version >= SDHCI_SPEC_400)
+               __sdhci_uhs2_add_host_v4(host, host->caps1);
+
+       if ((mmc->caps2 & MMC_CAP2_SD_UHS2) && !host->v4_mode)
+               /* host doesn't want to enable UHS2 support */
+               mmc->caps2 &= ~MMC_CAP2_SD_UHS2;
+
+       /* LED support not implemented for UHS2 */
+       host->quirks |= SDHCI_QUIRK_NO_LED;
+
+       ret = __sdhci_add_host(host);
+       if (ret)
+               goto cleanup;
+
+       return 0;
+
+cleanup:
+       if (host->version >= SDHCI_SPEC_400)
+               __sdhci_uhs2_remove_host(host, 0);
+
+       sdhci_cleanup_host(host);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_add_host);
+
+void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead)
+{
+       __sdhci_uhs2_remove_host(host, dead);
+
+       sdhci_remove_host(host, dead);
+}
+EXPORT_SYMBOL_GPL(sdhci_uhs2_remove_host);
+
 MODULE_AUTHOR("Intel, Genesys Logic, Linaro");
 MODULE_DESCRIPTION("MMC UHS-II Support");
 MODULE_LICENSE("GPL");
index d91474e..b5a0ec7 100644 (file)
@@ -179,5 +179,7 @@ void sdhci_uhs2_dump_regs(struct sdhci_host *host);
 void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask);
 void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd);
 void sdhci_uhs2_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
+int sdhci_uhs2_add_host(struct sdhci_host *host);
+void sdhci_uhs2_remove_host(struct sdhci_host *host, int dead);
 
 #endif /* __SDHCI_UHS2_H */