mmc: omap_hsmmc: add signal voltage selection support
I/O data lines of UHS SD card operates at 1.8V when in UHS speed mode (same is true for eMMC in DDR and HS200 modes). Add support to switch signal voltage to 1.8V in order to support UHS cards and eMMC HS200 and DDR modes. Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
This commit is contained in:
parent
90321dce0d
commit
04f9f8be83
2 changed files with 160 additions and 26 deletions
|
@ -172,10 +172,6 @@ struct omap_hsmmc_plat {
|
||||||
#define VS30_3V0SUP BIT(25)
|
#define VS30_3V0SUP BIT(25)
|
||||||
#define VS18_1V8SUP BIT(26)
|
#define VS18_1V8SUP BIT(26)
|
||||||
|
|
||||||
#define IOV_3V3 3300000
|
|
||||||
#define IOV_3V0 3000000
|
|
||||||
#define IOV_1V8 1800000
|
|
||||||
|
|
||||||
#define AC12_ET BIT(22)
|
#define AC12_ET BIT(22)
|
||||||
#define AC12_V1V8_SIGEN BIT(19)
|
#define AC12_V1V8_SIGEN BIT(19)
|
||||||
#define AC12_SCLK_SEL BIT(23)
|
#define AC12_SCLK_SEL BIT(23)
|
||||||
|
@ -224,6 +220,12 @@ struct omap_hsmmc_plat {
|
||||||
IE_DTO | IE_CIE | IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO |\
|
IE_DTO | IE_CIE | IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO |\
|
||||||
IE_BRR | IE_BWR | IE_TC | IE_CC)
|
IE_BRR | IE_BWR | IE_TC | IE_CC)
|
||||||
|
|
||||||
|
#define CON_CLKEXTFREE BIT(16)
|
||||||
|
#define CON_PADEN BIT(15)
|
||||||
|
#define PSTATE_CLEV BIT(24)
|
||||||
|
#define PSTATE_DLEV (0xF << 20)
|
||||||
|
#define PSTATE_DLEV_DAT0 (0x1 << 20)
|
||||||
|
|
||||||
int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio,
|
int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio,
|
||||||
int wp_gpio);
|
int wp_gpio);
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ struct omap_hsmmc_data {
|
||||||
#endif
|
#endif
|
||||||
uint bus_width;
|
uint bus_width;
|
||||||
uint clock;
|
uint clock;
|
||||||
|
ushort last_cmd;
|
||||||
#ifdef OMAP_HSMMC_USE_GPIO
|
#ifdef OMAP_HSMMC_USE_GPIO
|
||||||
#if CONFIG_IS_ENABLED(DM_MMC)
|
#if CONFIG_IS_ENABLED(DM_MMC)
|
||||||
struct gpio_desc cd_gpio; /* Change Detect GPIO */
|
struct gpio_desc cd_gpio; /* Change Detect GPIO */
|
||||||
|
@ -89,7 +90,6 @@ struct omap_hsmmc_data {
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if CONFIG_IS_ENABLED(DM_MMC)
|
#if CONFIG_IS_ENABLED(DM_MMC)
|
||||||
uint iov;
|
|
||||||
enum bus_mode mode;
|
enum bus_mode mode;
|
||||||
#endif
|
#endif
|
||||||
u8 controller_flags;
|
u8 controller_flags;
|
||||||
|
@ -98,6 +98,8 @@ struct omap_hsmmc_data {
|
||||||
uint desc_slot;
|
uint desc_slot;
|
||||||
#endif
|
#endif
|
||||||
const char *hw_rev;
|
const char *hw_rev;
|
||||||
|
struct udevice *pbias_supply;
|
||||||
|
uint signal_voltage;
|
||||||
#ifdef CONFIG_IODELAY_RECALIBRATION
|
#ifdef CONFIG_IODELAY_RECALIBRATION
|
||||||
struct omap_hsmmc_pinctrl_state *default_pinctrl_state;
|
struct omap_hsmmc_pinctrl_state *default_pinctrl_state;
|
||||||
struct omap_hsmmc_pinctrl_state *hs_pinctrl_state;
|
struct omap_hsmmc_pinctrl_state *hs_pinctrl_state;
|
||||||
|
@ -254,7 +256,8 @@ static unsigned char mmc_board_init(struct mmc *mmc)
|
||||||
&prcm_base->iclken1_core);
|
&prcm_base->iclken1_core);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)
|
#if (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) &&\
|
||||||
|
!CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||||
/* PBIAS config needed for MMC1 only */
|
/* PBIAS config needed for MMC1 only */
|
||||||
if (mmc_get_blk_desc(mmc)->devnum == 0)
|
if (mmc_get_blk_desc(mmc)->devnum == 0)
|
||||||
vmmc_pbias_config(LDO_VOLT_3V0);
|
vmmc_pbias_config(LDO_VOLT_3V0);
|
||||||
|
@ -398,32 +401,148 @@ static void omap_hsmmc_set_timing(struct mmc *mmc)
|
||||||
omap_hsmmc_start_clock(mmc_base);
|
omap_hsmmc_start_clock(mmc_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_hsmmc_conf_bus_power(struct mmc *mmc)
|
static void omap_hsmmc_conf_bus_power(struct mmc *mmc, uint signal_voltage)
|
||||||
{
|
{
|
||||||
struct hsmmc *mmc_base;
|
struct hsmmc *mmc_base;
|
||||||
struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
||||||
u32 val;
|
u32 hctl, ac12;
|
||||||
|
|
||||||
mmc_base = priv->base_addr;
|
mmc_base = priv->base_addr;
|
||||||
|
|
||||||
val = readl(&mmc_base->hctl) & ~SDVS_MASK;
|
hctl = readl(&mmc_base->hctl) & ~SDVS_MASK;
|
||||||
|
ac12 = readl(&mmc_base->ac12) & ~AC12_V1V8_SIGEN;
|
||||||
|
|
||||||
switch (priv->iov) {
|
switch (signal_voltage) {
|
||||||
case IOV_3V3:
|
case MMC_SIGNAL_VOLTAGE_330:
|
||||||
val |= SDVS_3V3;
|
hctl |= SDVS_3V0;
|
||||||
break;
|
break;
|
||||||
case IOV_3V0:
|
case MMC_SIGNAL_VOLTAGE_180:
|
||||||
val |= SDVS_3V0;
|
hctl |= SDVS_1V8;
|
||||||
break;
|
ac12 |= AC12_V1V8_SIGEN;
|
||||||
case IOV_1V8:
|
|
||||||
val |= SDVS_1V8;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(val, &mmc_base->hctl);
|
writel(hctl, &mmc_base->hctl);
|
||||||
|
writel(ac12, &mmc_base->ac12);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_hsmmc_set_capabilities(struct mmc *mmc)
|
#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
|
||||||
|
static int omap_hsmmc_wait_dat0(struct udevice *dev, int state, int timeout)
|
||||||
|
{
|
||||||
|
int ret = -ETIMEDOUT;
|
||||||
|
u32 con;
|
||||||
|
bool dat0_high;
|
||||||
|
bool target_dat0_high = !!state;
|
||||||
|
struct omap_hsmmc_data *priv = dev_get_priv(dev);
|
||||||
|
struct hsmmc *mmc_base = priv->base_addr;
|
||||||
|
|
||||||
|
con = readl(&mmc_base->con);
|
||||||
|
writel(con | CON_CLKEXTFREE | CON_PADEN, &mmc_base->con);
|
||||||
|
|
||||||
|
timeout = DIV_ROUND_UP(timeout, 10); /* check every 10 us. */
|
||||||
|
while (timeout--) {
|
||||||
|
dat0_high = !!(readl(&mmc_base->pstate) & PSTATE_DLEV_DAT0);
|
||||||
|
if (dat0_high == target_dat0_high) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
udelay(10);
|
||||||
|
}
|
||||||
|
writel(con, &mmc_base->con);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE)
|
||||||
|
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||||
|
static int omap_hsmmc_set_io_regulator(struct mmc *mmc, int mV)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int uV = mV * 1000;
|
||||||
|
|
||||||
|
struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
||||||
|
|
||||||
|
if (!mmc->vqmmc_supply)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Disable PBIAS */
|
||||||
|
ret = regulator_set_enable(priv->pbias_supply, false);
|
||||||
|
if (ret && ret != -ENOSYS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Turn off IO voltage */
|
||||||
|
ret = regulator_set_enable(mmc->vqmmc_supply, false);
|
||||||
|
if (ret && ret != -ENOSYS)
|
||||||
|
return ret;
|
||||||
|
/* Program a new IO voltage value */
|
||||||
|
ret = regulator_set_value(mmc->vqmmc_supply, uV);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
/* Turn on IO voltage */
|
||||||
|
ret = regulator_set_enable(mmc->vqmmc_supply, true);
|
||||||
|
if (ret && ret != -ENOSYS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Program PBIAS voltage*/
|
||||||
|
ret = regulator_set_value(priv->pbias_supply, uV);
|
||||||
|
if (ret && ret != -ENOSYS)
|
||||||
|
return ret;
|
||||||
|
/* Enable PBIAS */
|
||||||
|
ret = regulator_set_enable(priv->pbias_supply, true);
|
||||||
|
if (ret && ret != -ENOSYS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int omap_hsmmc_set_signal_voltage(struct mmc *mmc)
|
||||||
|
{
|
||||||
|
struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
||||||
|
struct hsmmc *mmc_base = priv->base_addr;
|
||||||
|
int mv = mmc_voltage_to_mv(mmc->signal_voltage);
|
||||||
|
u32 capa_mask;
|
||||||
|
__maybe_unused u8 palmas_ldo_volt;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
if (mv < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||||
|
/* Use 3.0V rather than 3.3V */
|
||||||
|
mv = 3000;
|
||||||
|
capa_mask = VS30_3V0SUP;
|
||||||
|
palmas_ldo_volt = LDO_VOLT_3V0;
|
||||||
|
} else if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||||
|
capa_mask = VS18_1V8SUP;
|
||||||
|
palmas_ldo_volt = LDO_VOLT_1V8;
|
||||||
|
} else {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = readl(&mmc_base->capa);
|
||||||
|
if (!(val & capa_mask))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
priv->signal_voltage = mmc->signal_voltage;
|
||||||
|
|
||||||
|
omap_hsmmc_conf_bus_power(mmc, mmc->signal_voltage);
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||||
|
return omap_hsmmc_set_io_regulator(mmc, mv);
|
||||||
|
#elif (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) && \
|
||||||
|
defined(CONFIG_PALMAS_POWER)
|
||||||
|
if (mmc_get_blk_desc(mmc)->devnum == 0)
|
||||||
|
vmmc_pbias_config(palmas_ldo_volt);
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint32_t omap_hsmmc_set_capabilities(struct mmc *mmc)
|
||||||
{
|
{
|
||||||
struct hsmmc *mmc_base;
|
struct hsmmc *mmc_base;
|
||||||
struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
||||||
|
@ -434,18 +553,17 @@ static void omap_hsmmc_set_capabilities(struct mmc *mmc)
|
||||||
|
|
||||||
if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
|
if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
|
||||||
val |= (VS30_3V0SUP | VS18_1V8SUP);
|
val |= (VS30_3V0SUP | VS18_1V8SUP);
|
||||||
priv->iov = IOV_3V0;
|
|
||||||
} else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) {
|
} else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) {
|
||||||
val |= VS30_3V0SUP;
|
val |= VS30_3V0SUP;
|
||||||
val &= ~VS18_1V8SUP;
|
val &= ~VS18_1V8SUP;
|
||||||
priv->iov = IOV_3V0;
|
|
||||||
} else {
|
} else {
|
||||||
val |= VS18_1V8SUP;
|
val |= VS18_1V8SUP;
|
||||||
val &= ~VS30_3V0SUP;
|
val &= ~VS30_3V0SUP;
|
||||||
priv->iov = IOV_1V8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(val, &mmc_base->capa);
|
writel(val, &mmc_base->capa);
|
||||||
|
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MMC_SUPPORTS_TUNING
|
#ifdef MMC_SUPPORTS_TUNING
|
||||||
|
@ -630,8 +748,9 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CONFIG_IS_ENABLED(DM_MMC)
|
#if CONFIG_IS_ENABLED(DM_MMC)
|
||||||
omap_hsmmc_set_capabilities(mmc);
|
reg_val = omap_hsmmc_set_capabilities(mmc);
|
||||||
omap_hsmmc_conf_bus_power(mmc);
|
omap_hsmmc_conf_bus_power(mmc, (reg_val & VS30_3V0SUP) ?
|
||||||
|
MMC_SIGNAL_VOLTAGE_330 : MMC_SIGNAL_VOLTAGE_180);
|
||||||
#else
|
#else
|
||||||
writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
|
writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
|
||||||
writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
|
writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
|
||||||
|
@ -842,6 +961,7 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
|
||||||
struct hsmmc *mmc_base;
|
struct hsmmc *mmc_base;
|
||||||
unsigned int flags, mmc_stat;
|
unsigned int flags, mmc_stat;
|
||||||
ulong start;
|
ulong start;
|
||||||
|
priv->last_cmd = cmd->cmdidx;
|
||||||
|
|
||||||
mmc_base = priv->base_addr;
|
mmc_base = priv->base_addr;
|
||||||
|
|
||||||
|
@ -1208,6 +1328,7 @@ static int omap_hsmmc_set_ios(struct udevice *dev)
|
||||||
struct mmc *mmc = upriv->mmc;
|
struct mmc *mmc = upriv->mmc;
|
||||||
#endif
|
#endif
|
||||||
struct hsmmc *mmc_base = priv->base_addr;
|
struct hsmmc *mmc_base = priv->base_addr;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (priv->bus_width != mmc->bus_width)
|
if (priv->bus_width != mmc->bus_width)
|
||||||
omap_hsmmc_set_bus_width(mmc);
|
omap_hsmmc_set_bus_width(mmc);
|
||||||
|
@ -1223,8 +1344,13 @@ static int omap_hsmmc_set_ios(struct udevice *dev)
|
||||||
#if CONFIG_IS_ENABLED(DM_MMC)
|
#if CONFIG_IS_ENABLED(DM_MMC)
|
||||||
if (priv->mode != mmc->selected_mode)
|
if (priv->mode != mmc->selected_mode)
|
||||||
omap_hsmmc_set_timing(mmc);
|
omap_hsmmc_set_timing(mmc);
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE)
|
||||||
|
if (priv->signal_voltage != mmc->signal_voltage)
|
||||||
|
ret = omap_hsmmc_set_signal_voltage(mmc);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
#endif
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef OMAP_HSMMC_USE_GPIO
|
#ifdef OMAP_HSMMC_USE_GPIO
|
||||||
|
@ -1298,6 +1424,9 @@ static const struct dm_mmc_ops omap_hsmmc_ops = {
|
||||||
.execute_tuning = omap_hsmmc_execute_tuning,
|
.execute_tuning = omap_hsmmc_execute_tuning,
|
||||||
#endif
|
#endif
|
||||||
.send_init_stream = omap_hsmmc_send_init_stream,
|
.send_init_stream = omap_hsmmc_send_init_stream,
|
||||||
|
#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
|
||||||
|
.wait_dat0 = omap_hsmmc_wait_dat0,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
static const struct mmc_ops omap_hsmmc_ops = {
|
static const struct mmc_ops omap_hsmmc_ops = {
|
||||||
|
@ -1759,7 +1888,10 @@ static int omap_hsmmc_probe(struct udevice *dev)
|
||||||
if (mmc == NULL)
|
if (mmc == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
|
#if CONFIG_IS_ENABLED(DM_REGULATOR)
|
||||||
|
device_get_supply_regulator(dev, "pbias-supply",
|
||||||
|
&priv->pbias_supply);
|
||||||
|
#endif
|
||||||
#if defined(OMAP_HSMMC_USE_GPIO) && CONFIG_IS_ENABLED(OF_CONTROL)
|
#if defined(OMAP_HSMMC_USE_GPIO) && CONFIG_IS_ENABLED(OF_CONTROL)
|
||||||
gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
|
gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
|
||||||
gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN);
|
gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN);
|
||||||
|
|
Loading…
Reference in a new issue