[U-Boot] [PATCH 4/5] mmc: fsl_esdhc: support SDR104 and HS200
Peng Fan
van.freenix at gmail.com
Sat Jan 20 07:05:41 UTC 2018
On Fri, Jan 19, 2018 at 08:23:46PM +0900, Jaehoon Chung wrote:
>On 01/19/2018 06:09 PM, Peng Fan wrote:
>> Introduce SDR104 and HS200 support
>> The implementation takes linux kernel sdhci.c and sdhci-esdhc-imx.c
>> as reference.
>> - Implement esdhc_change_pinstate to dynamically change pad settings
>> - Implement esdhc_set_timing
>> - Implement esdhc_set_voltage to switch voltage
>> - Implement fsl_esdhc_execute_tuning to execute time process
>> - Enlarge the cfg->f_max to 200MHz.
>> - Parse fsl,tuning-step, fsl,tuning-start-tap and
>> fsl,strobe-dll-delay-target from device tree.
>> - Parse no-1-8-v property
>> - Introduce esdhc_soc_data to indicate the flags and caps
>>
>> Signed-off-by: Peng Fan <peng.fan at nxp.com>
>> Cc: Stefano Babic <sbabic at denx.de>
>> Cc: Fabio Estevam <fabio.estevam at nxp.com>
>> Cc: Jaehoon Chung <jh80.chung at samsung.com>
>> ---
>> drivers/mmc/fsl_esdhc.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++-
>> include/fsl_esdhc.h | 47 +++++++
>> 2 files changed, 401 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
>> index f6279307d8..bd951311cf 100644
>> --- a/drivers/mmc/fsl_esdhc.c
>> +++ b/drivers/mmc/fsl_esdhc.c
>> @@ -23,6 +23,7 @@
>> #include <asm/io.h>
>> #include <dm.h>
>> #include <asm-generic/gpio.h>
>> +#include <dm/pinctrl.h>
>>
>> DECLARE_GLOBAL_DATA_PTR;
>>
>> @@ -90,6 +91,11 @@ struct fsl_esdhc_plat {
>> struct mmc mmc;
>> };
>>
>> +struct esdhc_soc_data {
>> + u32 flags;
>> + u32 caps;
>> +};
>> +
>> /**
>> * struct fsl_esdhc_priv
>> *
>> @@ -103,12 +109,20 @@ struct fsl_esdhc_plat {
>> * @non_removable: 0: removable; 1: non-removable
>> * @wp_enable: 1: enable checking wp; 0: no check
>> * @vs18_enable: 1: use 1.8V voltage; 0: use 3.3V
>> + * @flags: ESDHC_FLAG_xx in include/fsl_esdhc.h
>> + * @caps: controller capabilities
>> + * @tuning_step: tuning step setting in tuning_ctrl register
>> + * @start_tuning_tap: the start point for tuning in tuning_ctrl register
>> + * @strobe_dll_delay_target: settings in strobe_dllctrl
>> + * @signal_voltage: indicating the current voltage
>> * @cd_gpio: gpio for card detection
>> * @wp_gpio: gpio for write protection
>> */
>> struct fsl_esdhc_priv {
>> struct fsl_esdhc *esdhc_regs;
>> unsigned int sdhc_clk;
>> + unsigned int clock;
>> + unsigned int mode;
>> unsigned int bus_width;
>> #if !CONFIG_IS_ENABLED(BLK)
>> struct mmc *mmc;
>> @@ -117,6 +131,16 @@ struct fsl_esdhc_priv {
>> int non_removable;
>> int wp_enable;
>> int vs18_enable;
>> + u32 flags;
>> + u32 caps;
>> + u32 tuning_step;
>> + u32 tuning_start_tap;
>> + u32 strobe_dll_delay_target;
>> + u32 signal_voltage;
>> +#if IS_ENABLED(CONFIG_DM_REGULATOR)
>> + struct udevice *vqmmc_dev;
>> + struct udevice *vmmc_dev;
>> +#endif
>> #ifdef CONFIG_DM_GPIO
>> struct gpio_desc cd_gpio;
>> struct gpio_desc wp_gpio;
>> @@ -364,6 +388,7 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
>> int err = 0;
>> uint xfertyp;
>> uint irqstat;
>> + u32 flags;
>> struct fsl_esdhc *regs = priv->esdhc_regs;
>>
>> #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
>> @@ -417,8 +442,15 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
>> esdhc_write32(®s->xfertyp, xfertyp);
>> #endif
>>
>> + if ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK) ||
>> + (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)) {
>> + flags = IRQSTAT_BRR;
>> + } else {
>> + flags = IRQSTAT_CC | IRQSTAT_CTOE;
>> + }
>
>IRQSTAT_CC | IRQSTAT_CTOE can be set to default value like below.
>
>int flags = IRQSTAT_CC | IRQSTAT_CTOE;
>
>> +
>> /* Wait for the command to complete */
>> - while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE)))
>> + while (!(esdhc_read32(®s->irqstat) & flags))
>> ;
>>
>> irqstat = esdhc_read32(®s->irqstat);
>> @@ -480,6 +512,12 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
>> #ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO
>> esdhc_pio_read_write(priv, data);
>> #else
>> + flags = DATA_COMPLETE;
>> + if ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK) ||
>> + (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)) {
>> + flags = IRQSTAT_BRR;
>> + }
>> +
>> do {
>> irqstat = esdhc_read32(®s->irqstat);
>>
>> @@ -492,7 +530,7 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
>> err = -ECOMM;
>> goto out;
>> }
>> - } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE);
>> + } while ((irqstat & flags) != flags);
>>
>> /*
>> * Need invalidate the dcache here again to avoid any
>> @@ -573,6 +611,7 @@ static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock)
>> esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN);
>> #endif
>>
>> + priv->clock = clock;
>> }
>>
>> #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
>> @@ -604,9 +643,233 @@ static void esdhc_clock_control(struct fsl_esdhc_priv *priv, bool enable)
>> }
>> #endif
>>
>> +#ifdef MMC_SUPPORTS_TUNING
>> +static int esdhc_change_pinstate(struct udevice *dev)
>> +{
>> + struct fsl_esdhc_priv *priv = dev_get_priv(dev);
>> + int ret;
>> +
>> + switch (priv->mode) {
>> + case UHS_SDR50:
>> + case UHS_DDR50:
>> + ret = pinctrl_select_state(dev, "state_100mhz");
>> + break;
>> + case UHS_SDR104:
>> + case MMC_HS_200:
>> + ret = pinctrl_select_state(dev, "state_200mhz");
>> + break;
>> + default:
>> + ret = pinctrl_select_state(dev, "default");
>> + break;
>> + }
>> +
>> + if (ret)
>> + printf("%s %d error\n", __func__, priv->mode);
>> +
>> + return ret;
>> +}
>> +
>> +static void esdhc_reset_tuning(struct mmc *mmc)
>> +{
>> + struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev);
>> + struct fsl_esdhc *regs = priv->esdhc_regs;
>> +
>> + if (priv->flags & ESDHC_FLAG_USDHC) {
>> + if (priv->flags & ESDHC_FLAG_STD_TUNING) {
>
>Don't need to use "if" condition at here.
>if (priv->flags & (ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING) ?
This is another condition is to check ESDHC_FLAG_MAN_TUNING.
In this patch I did not add that support.
So let's keep as it is for later add MAN tuning.
>
>> + esdhc_clrbits32(®s->autoc12err,
>> + MIX_CTRL_SMPCLK_SEL |
>> + MIX_CTRL_EXE_TUNE);
>> + }
>> + }
>> +}
>> +
>> +static int esdhc_set_timing(struct mmc *mmc)
>> +{
>> + struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev);
>> + struct fsl_esdhc *regs = priv->esdhc_regs;
>> + u32 m;
>
>Ues the meaningful variable name, not just "m".
use mixctrl in v2.
>
>> +
>> + m = readl(®s->mixctrl);
>> + m &= ~(MIX_CTRL_DDREN | MIX_CTRL_HS400_EN);
>> +
>> + switch (mmc->selected_mode) {
>> + case MMC_LEGACY:
>> + case SD_LEGACY:
>> + esdhc_reset_tuning(mmc);
>> + break;
>> + case MMC_HS:
>> + case MMC_HS_52:
>> + case MMC_HS_200:
>> + case SD_HS:
>> + case UHS_SDR12:
>> + case UHS_SDR25:
>> + case UHS_SDR50:
>> + case UHS_SDR104:
>> + writel(m, ®s->mixctrl);
>> + break;
>> + case UHS_DDR50:
>> + case MMC_DDR_52:
>> + m |= MIX_CTRL_DDREN;
>> + writel(m, ®s->mixctrl);
>> + break;
>> + default:
>> + printf("Not supported %d\n", mmc->selected_mode);
>
>Doesn't need to return? Does it need to call esdhc_change_pinstate()?
need return here. Fix in V2.
>
>> + break;
>> + }
>> +
>> + priv->mode = mmc->selected_mode;
>> +
>> + return esdhc_change_pinstate(mmc->dev);
>> +}
>> +
>> +static int esdhc_set_voltage(struct mmc *mmc)
>> +{
>> + struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev);
>> + struct fsl_esdhc *regs = priv->esdhc_regs;
>> + int ret;
>> +
>> + priv->signal_voltage = mmc->signal_voltage;
>> + switch (mmc->signal_voltage) {
>> + case MMC_SIGNAL_VOLTAGE_330:
>> + if (priv->vs18_enable)
>> + return -EIO;
>> + if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) {
>
>Not need to check the CONFIG_DM_REGULATOR for vqmmc_dev and vmmc_dev?
The support for UHS or HS200 assumes DM_REGULATOR enabled. If no regulator
support, the UHS or HS200 will not work.
>
>> + ret = regulator_set_value(priv->vqmmc_dev, 3300000);
>> + if (ret) {
>> + printf("Setting to 3.3V error");
>> + return -EIO;
>> + }
>> + /* Wait for 5ms */
>> + mdelay(5);
>> + }
>> +
>> + esdhc_clrbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT);
>> + if (!(esdhc_read32(®s->vendorspec) &
>> + ESDHC_VENDORSPEC_VSELECT))
>> + return 0;
>> +
>> + return -EAGAIN;
>> + case MMC_SIGNAL_VOLTAGE_180:
>> + if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) {
>> + ret = regulator_set_value(priv->vqmmc_dev, 1800000);
>> + if (ret) {
>> + printf("Setting to 1.8V error");
>> + return -EIO;
>> + }
>> + }
>> + esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT);
>> + if (esdhc_read32(®s->vendorspec) & ESDHC_VENDORSPEC_VSELECT)
>> + return 0;
>> +
>> + return -EAGAIN;
>> + case MMC_SIGNAL_VOLTAGE_120:
>> + return -ENOTSUPP;
>> + default:
>> + return 0;
>> + }
>> +}
>> +
>> +static void esdhc_stop_tuning(struct mmc *mmc)
>> +{
>> + struct mmc_cmd cmd;
>> +
>> + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
>> + cmd.cmdarg = 0;
>> + cmd.resp_type = MMC_RSP_R1b;
>> +
>> + dm_mmc_send_cmd(mmc->dev, &cmd, NULL);
>> +}
>> +
>> +static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
>> +{
>> + struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
>> + struct fsl_esdhc_priv *priv = dev_get_priv(dev);
>> + struct fsl_esdhc *regs = priv->esdhc_regs;
>> + struct mmc *mmc = &plat->mmc;
>> + u32 irqstaten = readl(®s->irqstaten);
>> + u32 irqsigen = readl(®s->irqsigen);
>> + int i, ret = -ETIMEDOUT;
>> + u32 v, m;
>
>Ditto.
fix in V2.
>
>> +
>> + /* clock tuning is not needed for upto 52MHz */
>> + if (mmc->clock <= 52000000)
>> + return 0;
>> +
>> + /* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */
>> + if (priv->flags & ESDHC_FLAG_STD_TUNING) {
>> + v = readl(®s->autoc12err);
>> + m = readl(®s->mixctrl);
>> + v &= ~MIX_CTRL_SMPCLK_SEL;
>> + m &= ~MIX_CTRL_FBCLK_SEL;
>> + m &= ~MIX_CTRL_AUTO_TUNE_EN;
>
>Combine to one line.
Fix in V2.
>
>> +
>> + v |= MIX_CTRL_EXE_TUNE;
>> + m |= MIX_CTRL_FBCLK_SEL;
>> + m |= MIX_CTRL_AUTO_TUNE_EN;
>
>Ditto.
Fix in V2.
>
>> +
>> + writel(v, ®s->autoc12err);
>> + writel(m, ®s->mixctrl);
>> + }
>> +
>> + /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); */
>> + v = readl(®s->mixctrl);
>> + v = MIX_CTRL_DTDSEL_READ | (v & ~MIX_CTRL_SDHCI_MASK);
>> + writel(v, ®s->mixctrl);
>> +
>> + writel(IRQSTATEN_BRR, ®s->irqstaten);
>> + writel(IRQSTATEN_BRR, ®s->irqsigen);
>> +
>> + for (i = 0; i < 40; i++) {
>
>What is 40? don't use the magic number.
Fix in V2.
>
>> + u32 ctrl;
>> +
>> + if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200) {
>> + if (mmc->bus_width == 8)
>> + writel(0x7080, ®s->blkattr);
>> + else if (mmc->bus_width == 4)
>> + writel(0x7040, ®s->blkattr);
>> + } else {
>> + writel(0x7040, ®s->blkattr);
>> + }
>> +
>> + /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE) */
>> + v = readl(®s->mixctrl);
>> + v = MIX_CTRL_DTDSEL_READ | (v & ~MIX_CTRL_SDHCI_MASK);
>> + writel(v, ®s->mixctrl);
>> +
>> + /* We are using STD tuning, no need to check return value */
>> + mmc_send_tuning(mmc, opcode, NULL);
>
>mmc_send_tuning will have the return value, not need to check?
You could see the following code, checking EXE_TUNE and SMPCLK_SEL.
The two bits are indicating tuning sucess or failure.
>
>> +
>> + ctrl = readl(®s->autoc12err);
>> + if ((!(ctrl & MIX_CTRL_EXE_TUNE)) &&
>> + (ctrl & MIX_CTRL_SMPCLK_SEL)) {
>> + /*
>> + * need to wait some time, make sure sd/mmc fininsh
>> + * send out tuning data, otherwise, the sd/mmc can't
>> + * response to any command when the card still out
>> + * put the tuning data.
>> + */
>> + mdelay(1);
>> + ret = 0;
>> + break;
>> + }
>> +
>> + /* Add 1ms delay for SD and eMMC */
>> + mdelay(1);
>> + }
>> +
>> + writel(irqstaten, ®s->irqstaten);
>> + writel(irqsigen, ®s->irqsigen);
>> +
>> + esdhc_stop_tuning(mmc);
>> +
>> + return ret;
>> +}
>> +#endif
>> +
>> static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
>> {
>> struct fsl_esdhc *regs = priv->esdhc_regs;
>> + int ret __maybe_unused;
>>
>> #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
>> /* Select to use peripheral clock */
>> @@ -615,7 +878,41 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
>> esdhc_clock_control(priv, true);
>> #endif
>> /* Set the clock speed */
>> - set_sysctl(priv, mmc, mmc->clock);
>> + if (priv->clock != mmc->clock)
>> + set_sysctl(priv, mmc, mmc->clock);
>> +
>> +#ifdef MMC_SUPPORTS_TUNING
>> + if (mmc->clk_disable) {
>> +#ifdef CONFIG_FSL_USDHC
>> + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN);
>> +#else
>> + esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN);
>> +#endif
>> + } else {
>> +#ifdef CONFIG_FSL_USDHC
>> + esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN |
>> + VENDORSPEC_CKEN);
>> +#else
>> + esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN);
>> +#endif
>> + }
>> +
>> + if (priv->mode != mmc->selected_mode) {
>> + ret = esdhc_set_timing(mmc);
>> + if (ret) {
>> + printf("esdhc_set_timing error %d\n", ret);
>> + return ret;
>> + }
>> + }
>> +
>> + if (priv->signal_voltage != mmc->signal_voltage) {
>> + ret = esdhc_set_voltage(mmc);
>> + if (ret) {
>> + printf("esdhc_set_voltage error %d\n", ret);
>> + return ret;
>> + }
>> + }
>> +#endif
>>
>> /* Set the bus width */
>> esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8);
>> @@ -790,6 +1087,10 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv,
>> #ifndef CONFIG_FSL_USDHC
>> esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN
>> | SYSCTL_IPGEN | SYSCTL_CKEN);
>> + /* Clearing tuning bits in case ROM has set it already */
>> + esdhc_write32(®s->mixctrl, 0);
>> + esdhc_write32(®s->autoc12err, 0);
>> + esdhc_write32(®s->clktunectrlstatus, 0);
>> #else
>> esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN |
>> VENDORSPEC_HCKEN | VENDORSPEC_IPGEN | VENDORSPEC_CKEN);
>> @@ -863,11 +1164,27 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv,
>> cfg->host_caps &= ~MMC_MODE_8BIT;
>> #endif
>>
>> + cfg->host_caps |= priv->caps;
>> +
>> cfg->f_min = 400000;
>> - cfg->f_max = min(priv->sdhc_clk, (u32)52000000);
>> + cfg->f_max = min(priv->sdhc_clk, (u32)200000000);
>>
>> cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
>>
>> + writel(0, ®s->dllctrl);
>> + if (priv->flags & ESDHC_FLAG_USDHC) {
>> + if (priv->flags & ESDHC_FLAG_STD_TUNING) {
>
>Ditto.
This is for later to introduce MAN tuning, need add an new checking..
THanks,
Peng
More information about the U-Boot
mailing list