mmc: jz4740: Add support for vqmmc power supply
authorPaul Cercueil <paul@crapouillou.net>
Tue, 31 Jan 2023 21:02:29 +0000 (21:02 +0000)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 13 Feb 2023 23:27:13 +0000 (00:27 +0100)
Support enabling / disabling the vqmmc power supply if it was provided
by the firmware.

Provide the .start_signal_voltage_switch callback to change the voltage
of the external vqmmc power supply.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Link: https://lore.kernel.org/r/20230131210229.68129-2-paul@crapouillou.net
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/jz4740_mmc.c

index eda1e2ddcaca8c9ca4822b899b6f1702c46aef50..698450afa7bb603fee589f332db6aa73052fb5f5 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/scatterlist.h>
 
 #include <asm/cacheflush.h>
@@ -158,6 +159,8 @@ struct jz4740_mmc_host {
        struct mmc_request *req;
        struct mmc_command *cmd;
 
+       bool vqmmc_enabled;
+
        unsigned long waiting;
 
        uint32_t cmdat;
@@ -935,6 +938,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
 static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct jz4740_mmc_host *host = mmc_priv(mmc);
+       int ret;
+
        if (ios->clock)
                jz4740_mmc_set_clock_rate(host, ios->clock);
 
@@ -947,12 +952,25 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                clk_prepare_enable(host->clk);
                break;
        case MMC_POWER_ON:
+               if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+                       ret = regulator_enable(mmc->supply.vqmmc);
+                       if (ret)
+                               dev_err(&host->pdev->dev, "Failed to set vqmmc power!\n");
+                       else
+                               host->vqmmc_enabled = true;
+               }
                break;
-       default:
+       case MMC_POWER_OFF:
                if (!IS_ERR(mmc->supply.vmmc))
                        mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+               if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
+                       regulator_disable(mmc->supply.vqmmc);
+                       host->vqmmc_enabled = false;
+               }
                clk_disable_unprepare(host->clk);
                break;
+       default:
+               break;
        }
 
        switch (ios->bus_width) {
@@ -978,6 +996,23 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
        jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable);
 }
 
+static int jz4740_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       int ret;
+
+       /* vqmmc regulator is available */
+       if (!IS_ERR(mmc->supply.vqmmc)) {
+               ret = mmc_regulator_set_vqmmc(mmc, ios);
+               return ret < 0 ? ret : 0;
+       }
+
+       /* no vqmmc regulator, assume fixed regulator at 3/3.3V */
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+               return 0;
+
+       return -EINVAL;
+}
+
 static const struct mmc_host_ops jz4740_mmc_ops = {
        .request        = jz4740_mmc_request,
        .pre_req        = jz4740_mmc_pre_request,
@@ -986,6 +1021,7 @@ static const struct mmc_host_ops jz4740_mmc_ops = {
        .get_ro         = mmc_gpio_get_ro,
        .get_cd         = mmc_gpio_get_cd,
        .enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
+       .start_signal_voltage_switch = jz4740_voltage_switch,
 };
 
 static const struct of_device_id jz4740_mmc_of_match[] = {