From: Peng Fan <van.freenix@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 4/5] mmc: fsl_esdhc: support SDR104 and HS200
Date: Sat, 20 Jan 2018 15:05:41 +0800 [thread overview]
Message-ID: <20180120070541.GA20412@shlinux2> (raw)
In-Reply-To: <d38b5c86-4843-5d65-dea0-aeb49dbd7d59@samsung.com>
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@nxp.com>
>> Cc: Stefano Babic <sbabic@denx.de>
>> Cc: Fabio Estevam <fabio.estevam@nxp.com>
>> Cc: Jaehoon Chung <jh80.chung@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
next prev parent reply other threads:[~2018-01-20 7:05 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-01-19 9:09 [U-Boot] [PATCH 1/5] imx: mx7: change sdhc clk to 392M Peng Fan
2018-01-19 9:09 ` [U-Boot] [PATCH 2/5] mmc: fsl_esdhc: add strobe and tuning entry Peng Fan
2018-01-19 9:09 ` [U-Boot] [PATCH 3/5] ARM: dts: add pinmux and tuning settings for HS200/SDR104 Peng Fan
2018-01-19 9:09 ` [U-Boot] [PATCH 4/5] mmc: fsl_esdhc: support SDR104 and HS200 Peng Fan
2018-01-19 11:23 ` Jaehoon Chung
2018-01-20 7:05 ` Peng Fan [this message]
2018-01-19 9:09 ` [U-Boot] [PATCH 5/5] imx: mx7dsabresd: enable UHS " Peng Fan
2018-01-20 20:06 ` [U-Boot] [PATCH 1/5] imx: mx7: change sdhc clk to 392M Fabio Estevam
2018-01-21 2:31 ` Peng Fan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180120070541.GA20412@shlinux2 \
--to=van.freenix@gmail.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.