From mboxrd@z Thu Jan 1 00:00:00 1970 From: Peng Fan Date: Sat, 20 Jan 2018 15:05:41 +0800 Subject: [U-Boot] [PATCH 4/5] mmc: fsl_esdhc: support SDR104 and HS200 In-Reply-To: References: <20180119090922.32320-1-peng.fan@nxp.com> <20180119090922.32320-4-peng.fan@nxp.com> Message-ID: <20180120070541.GA20412@shlinux2> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 >> Cc: Stefano Babic >> Cc: Fabio Estevam >> Cc: Jaehoon Chung >> --- >> 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 >> #include >> #include >> +#include >> >> 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