mmc: msm_sdcc: set IO_PAD_PWR_SWITCH bit based on IO voltage level
SDCC4 controller has IO_PAD_PWR_SWITCH control bit in MCI_CLK register. Driver should set this bit to 1 if the IO voltage level is within low voltage range (1.7v - 1.9v) else clear it. But driver sets this bit to 1 only if the MMC core layer initiates the voltage switch sequence. So if voltage switch sequence is not initiated (which is the case with non UHS-I cards) then even if the IO voltage level is within low voltage range, IO_PAD_PWR_SWITCH bit remains cleared. This change checks the IO voltage level and based on that sets the IO_PAD_PWR_SWITCH bit. CRs-Fixed: 386445 Change-Id: Id0648b0f87ea9bcc942e855e30da810e0fe5ebf5 Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org> (cherry picked from commit 341b9e7dbaf0a3caf6e41d37486e3bccc9795feb)
This commit is contained in:
committed by
Stephen Boyd
parent
b620f87623
commit
996bb4c5dc
@@ -2191,6 +2191,18 @@ static inline int msmsdcc_vreg_set_voltage(struct msm_mmc_reg_data *vreg,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int msmsdcc_vreg_get_voltage(struct msm_mmc_reg_data *vreg)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = regulator_get_voltage(vreg->reg);
|
||||
if (rc < 0)
|
||||
pr_err("%s: regulator_get_voltage(%s) failed. rc=%d\n",
|
||||
__func__, vreg->name, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int msmsdcc_vreg_set_optimum_mode(struct msm_mmc_reg_data *vreg,
|
||||
int uA_load)
|
||||
{
|
||||
@@ -2435,6 +2447,82 @@ enum vdd_io_level {
|
||||
VDD_IO_SET_LEVEL,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function returns the current VDD IO voltage level.
|
||||
* Returns negative value if it fails to read the voltage level
|
||||
* Returns 0 if regulator was disabled or if VDD_IO (and VDD)
|
||||
* regulator were not defined for host.
|
||||
*/
|
||||
static int msmsdcc_get_vdd_io_vol(struct msmsdcc_host *host)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (host->plat->vreg_data) {
|
||||
struct msm_mmc_reg_data *io_reg =
|
||||
host->plat->vreg_data->vdd_io_data;
|
||||
|
||||
/*
|
||||
* If vdd_io is not defined, then we can consider that
|
||||
* IO voltage is same as VDD.
|
||||
*/
|
||||
if (!io_reg)
|
||||
io_reg = host->plat->vreg_data->vdd_data;
|
||||
|
||||
if (io_reg && io_reg->is_enabled)
|
||||
rc = msmsdcc_vreg_get_voltage(io_reg);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function updates the IO pad power switch bit in MCI_CLK register
|
||||
* based on currrent IO pad voltage level.
|
||||
* NOTE: This function assumes that host lock was not taken by caller.
|
||||
*/
|
||||
static void msmsdcc_update_io_pad_pwr_switch(struct msmsdcc_host *host)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!is_io_pad_pwr_switch(host))
|
||||
return;
|
||||
|
||||
rc = msmsdcc_get_vdd_io_vol(host);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
/*
|
||||
* Dual voltage pad is the SDCC's (chipset) functionality and not all
|
||||
* the SDCC instances support the dual voltage pads.
|
||||
* For dual-voltage pad (1.8v/3.3v), SW should set IO_PAD_PWR_SWITCH
|
||||
* bit before using the pads in 1.8V mode.
|
||||
* For regular, not dual-voltage pads (including eMMC 1.2v/1.8v pads),
|
||||
* IO_PAD_PWR_SWITCH bit is a don't care.
|
||||
* But we don't have an option to know (by reading some SDCC register)
|
||||
* that a particular SDCC instance supports dual voltage pads or not,
|
||||
* so we simply set the IO_PAD_PWR_SWITCH bit for low voltage IO
|
||||
* (1.8v/1.2v). For regular (not dual-voltage pads), this bit value
|
||||
* is anyway ignored.
|
||||
*/
|
||||
if (rc > 0 && rc < 2700000)
|
||||
host->io_pad_pwr_switch = 1;
|
||||
else
|
||||
host->io_pad_pwr_switch = 0;
|
||||
|
||||
if (atomic_read(&host->clks_on)) {
|
||||
if (host->io_pad_pwr_switch)
|
||||
writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
|
||||
IO_PAD_PWR_SWITCH),
|
||||
host->base + MMCICLOCK);
|
||||
else
|
||||
writel_relaxed((readl_relaxed(host->base + MMCICLOCK) &
|
||||
~IO_PAD_PWR_SWITCH),
|
||||
host->base + MMCICLOCK);
|
||||
msmsdcc_sync_reg_wr(host);
|
||||
}
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static int msmsdcc_set_vdd_io_vol(struct msmsdcc_host *host,
|
||||
enum vdd_io_level level,
|
||||
unsigned int voltage_level)
|
||||
@@ -2736,6 +2824,7 @@ static u32 msmsdcc_setup_pwr(struct msmsdcc_host *host, struct mmc_ios *ios)
|
||||
* present or during system suspend).
|
||||
*/
|
||||
msmsdcc_set_vdd_io_vol(host, VDD_IO_LOW, 0);
|
||||
msmsdcc_update_io_pad_pwr_switch(host);
|
||||
msmsdcc_setup_pins(host, false);
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
@@ -2744,6 +2833,7 @@ static u32 msmsdcc_setup_pwr(struct msmsdcc_host *host, struct mmc_ios *ios)
|
||||
msmsdcc_cfg_mpm_sdiowakeup(host, SDC_DAT1_ENABLE);
|
||||
|
||||
msmsdcc_set_vdd_io_vol(host, VDD_IO_HIGH, 0);
|
||||
msmsdcc_update_io_pad_pwr_switch(host);
|
||||
msmsdcc_setup_pins(host, true);
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
@@ -3156,10 +3246,6 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
/* Select free running MCLK as input clock of cm_dll_sdc4 */
|
||||
clk |= (2 << 23);
|
||||
|
||||
/* Clear IO_PAD_PWR_SWITCH while powering off the card */
|
||||
if (!ios->vdd)
|
||||
host->io_pad_pwr_switch = 0;
|
||||
|
||||
if (host->io_pad_pwr_switch)
|
||||
clk |= IO_PAD_PWR_SWITCH;
|
||||
|
||||
@@ -3442,14 +3528,12 @@ static int msmsdcc_switch_io_voltage(struct mmc_host *mmc,
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->io_pad_pwr_switch = 0;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
switch (ios->signal_voltage) {
|
||||
case MMC_SIGNAL_VOLTAGE_330:
|
||||
/* Set VDD IO to high voltage range (2.7v - 3.6v) */
|
||||
rc = msmsdcc_set_vdd_io_vol(host, VDD_IO_HIGH, 0);
|
||||
if (!rc)
|
||||
msmsdcc_update_io_pad_pwr_switch(host);
|
||||
goto out;
|
||||
case MMC_SIGNAL_VOLTAGE_180:
|
||||
break;
|
||||
@@ -3460,6 +3544,8 @@ static int msmsdcc_switch_io_voltage(struct mmc_host *mmc,
|
||||
* DDR 1.2V mode.
|
||||
*/
|
||||
rc = msmsdcc_set_vdd_io_vol(host, VDD_IO_SET_LEVEL, 1200000);
|
||||
if (!rc)
|
||||
msmsdcc_update_io_pad_pwr_switch(host);
|
||||
goto out;
|
||||
default:
|
||||
/* invalid selection. don't do anything */
|
||||
@@ -3498,12 +3584,7 @@ static int msmsdcc_switch_io_voltage(struct mmc_host *mmc,
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
|
||||
IO_PAD_PWR_SWITCH), host->base + MMCICLOCK);
|
||||
msmsdcc_sync_reg_wr(host);
|
||||
host->io_pad_pwr_switch = 1;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
msmsdcc_update_io_pad_pwr_switch(host);
|
||||
|
||||
/* Wait 5 ms for the voltage regulater in the card to become stable. */
|
||||
usleep_range(5000, 5500);
|
||||
|
||||
@@ -428,6 +428,7 @@ struct msmsdcc_host {
|
||||
#define MSMSDCC_SW_RST (1 << 5)
|
||||
#define MSMSDCC_SW_RST_CFG (1 << 6)
|
||||
#define MSMSDCC_WAIT_FOR_TX_RX (1 << 7)
|
||||
#define MSMSDCC_IO_PAD_PWR_SWITCH (1 << 8)
|
||||
|
||||
#define set_hw_caps(h, val) ((h)->hw_caps |= val)
|
||||
#define is_sps_mode(h) ((h)->hw_caps & MSMSDCC_SPS_BAM_SUP)
|
||||
@@ -438,6 +439,7 @@ struct msmsdcc_host {
|
||||
#define is_sw_hard_reset(h) ((h)->hw_caps & MSMSDCC_SW_RST)
|
||||
#define is_sw_reset_save_config(h) ((h)->hw_caps & MSMSDCC_SW_RST_CFG)
|
||||
#define is_wait_for_tx_rx_active(h) ((h)->hw_caps & MSMSDCC_WAIT_FOR_TX_RX)
|
||||
#define is_io_pad_pwr_switch(h) ((h)->hw_caps & MSMSDCC_IO_PAD_PWR_SWITCH)
|
||||
|
||||
/* Set controller capabilities based on version */
|
||||
static inline void set_default_hw_caps(struct msmsdcc_host *host)
|
||||
@@ -457,7 +459,7 @@ static inline void set_default_hw_caps(struct msmsdcc_host *host)
|
||||
if (version) /* SDCC v4 and greater */
|
||||
host->hw_caps |= MSMSDCC_AUTO_PROG_DONE |
|
||||
MSMSDCC_SOFT_RESET | MSMSDCC_REG_WR_ACTIVE
|
||||
| MSMSDCC_WAIT_FOR_TX_RX;
|
||||
| MSMSDCC_WAIT_FOR_TX_RX | MSMSDCC_IO_PAD_PWR_SWITCH;
|
||||
|
||||
if (version >= 0x2D) /* SDCC v4 2.1.0 and greater */
|
||||
host->hw_caps |= MSMSDCC_SW_RST | MSMSDCC_SW_RST_CFG;
|
||||
|
||||
Reference in New Issue
Block a user