Commit 6eb2c8e1 authored by Victor Shih's avatar Victor Shih Committed by Ulf Hansson
Browse files

mmc: sdhci-uhs2: add set_power() to support vdd2



This is a UHS-II version of sdhci's set_power operation.
Use sdhci_uhs2_set_power() to set VDD2 for support UHS2 interface.
VDD2, as well as VDD, is handled here.

Signed-off-by: default avatarBen Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: default avatarAKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: default avatarVictor Shih <victor.shih@genesyslogic.com.tw>
Acked-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Message-ID: <20241018105333.4569-7-victorshihgli@gmail.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 9b1c779d
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -59,6 +59,13 @@ EXPORT_SYMBOL_GPL(sdhci_uhs2_dump_regs);
 *                                                                           *
\*****************************************************************************/

static inline int mmc_opt_regulator_set_ocr(struct mmc_host *mmc,
					    struct regulator *supply,
					    unsigned short vdd_bit)
{
	return IS_ERR_OR_NULL(supply) ? 0 : mmc_regulator_set_ocr(mmc, supply, vdd_bit);
}

/**
 * sdhci_uhs2_reset - invoke SW reset
 * @host: SDHCI host
@@ -86,6 +93,48 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_reset);

void sdhci_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
{
	struct mmc_host *mmc = host->mmc;
	u8 pwr = 0;

	if (mode != MMC_POWER_OFF) {
		pwr = sdhci_get_vdd_value(vdd);
		if (!pwr)
			WARN(1, "%s: Invalid vdd %#x\n",
			     mmc_hostname(host->mmc), vdd);
		pwr |= SDHCI_VDD2_POWER_180;
	}

	if (host->pwr == pwr)
		return;
	host->pwr = pwr;

	if (pwr == 0) {
		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);

		mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
		mmc_regulator_set_vqmmc2(mmc, &mmc->ios);
	} else {
		mmc_opt_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
		/* support 1.8v only for now */
		mmc_regulator_set_vqmmc2(mmc, &mmc->ios);

		/* Clear the power reg before setting a new value */
		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);

		/* vdd first */
		pwr |= SDHCI_POWER_ON;
		sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
		mdelay(5);

		pwr |= SDHCI_VDD2_POWER_ON;
		sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
		mdelay(5);
	}
}
EXPORT_SYMBOL_GPL(sdhci_uhs2_set_power);

/*****************************************************************************\
 *                                                                           *
 * Driver init/exit                                                          *
+1 −0
Original line number Diff line number Diff line
@@ -176,5 +176,6 @@ struct sdhci_host;

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);

#endif /* __SDHCI_UHS2_H */
+33 −28
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>

#include <linux/bug.h>
#include <linux/leds.h>

#include <linux/mmc/mmc.h>
@@ -2061,12 +2061,8 @@ static void sdhci_set_power_reg(struct sdhci_host *host, unsigned char mode,
		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
}

void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
			   unsigned short vdd)
unsigned short sdhci_get_vdd_value(unsigned short vdd)
{
	u8 pwr = 0;

	if (mode != MMC_POWER_OFF) {
	switch (1 << vdd) {
	case MMC_VDD_165_195:
	/*
@@ -2076,26 +2072,35 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
	 * for the purpose of turning on the power.
	 */
	case MMC_VDD_20_21:
			pwr = SDHCI_POWER_180;
			break;
		return SDHCI_POWER_180;
	case MMC_VDD_29_30:
	case MMC_VDD_30_31:
			pwr = SDHCI_POWER_300;
			break;
		return SDHCI_POWER_300;
	case MMC_VDD_32_33:
	case MMC_VDD_33_34:
	/*
		 * 3.4 ~ 3.6V are valid only for those platforms where it's
	 * 3.4V ~ 3.6V are valid only for those platforms where it's
	 * known that the voltage range is supported by hardware.
	 */
	case MMC_VDD_34_35:
	case MMC_VDD_35_36:
			pwr = SDHCI_POWER_330;
			break;
		return SDHCI_POWER_330;
	default:
		return 0;
	}
}
EXPORT_SYMBOL_GPL(sdhci_get_vdd_value);

void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
			   unsigned short vdd)
{
	u8 pwr = 0;

	if (mode != MMC_POWER_OFF) {
		pwr = sdhci_get_vdd_value(vdd);
		if (!pwr) {
			WARN(1, "%s: Invalid vdd %#x\n",
			     mmc_hostname(host->mmc), vdd);
			break;
		}
	}

+1 −0
Original line number Diff line number Diff line
@@ -836,6 +836,7 @@ void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
				     unsigned char mode,
				     unsigned short vdd);
unsigned short sdhci_get_vdd_value(unsigned short vdd);
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
			   unsigned short vdd);
int sdhci_get_cd_nogpio(struct mmc_host *mmc);