From: Victor Shih <victor.shih@genesyslogic.com.tw>
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>
---
Updates in V22:
- Adjust the patch order from v21 patch#18 to v22 patch#14 and
remove unnecessary code to avoid defined but not used warnings.
Updates in V14:
- Use mmc_card_uhs2() to stead sdhci_uhs2_mode() in the
__sdhci_uhs2_remove_host().
Updates in V13:
- Re-order function to avoid declaration.
- Use vqmmc2 to stead vmmc2.
Updates in V11:
- Remove unused ocr_avail_uhs2.
Updates in V10:
- Move some definitions of PatchV9[05/23] to PatchV10[20/23].
Updates in V8:
- Change return type to void for __sdhci_uhs2_add_host_v4().
- Remove unused variables in __sdhci_uhs2_add_host_v4().
Updates in V7:
- __sdhci_add_host() to instead of __sdhci_uhs2_add_host()
in sdhci_uhs2_add_host().
- Cancel export state of some functions.
Updates in V6:
- Add complete_work_fn/thread_irq_fn variables in struct sdhci_host.
- Use complete_work_fn/thread_irq_fn variables in
sdhci_alloc_host() and sdhci_uhs2_add_host().
- Use sdhci_uhs2_mode() to simplify code in __sdhci_uhs2_remove_host().
---
drivers/mmc/host/sdhci-uhs2.c | 91 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-uhs2.h | 2 +
2 files changed, 93 insertions(+)
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index 6b249eb8395a..d3af620c7b68 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -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");
diff --git a/drivers/mmc/host/sdhci-uhs2.h b/drivers/mmc/host/sdhci-uhs2.h
index 9bd3f610cf8c..0e1866a6bdb6 100644
--- a/drivers/mmc/host/sdhci-uhs2.h
+++ b/drivers/mmc/host/sdhci-uhs2.h
@@ -181,5 +181,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 */
--
2.25.1