From 925f6900c8f51f2b9d4c5cd389be51c19121e974 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 22 Feb 2022 11:28:18 +0800 Subject: [PATCH 1/6] mmc: fsl_esdhc_imx: use VENDORSPEC_FRC_SDCLK_ON when necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit f132aab40327 ("Revert "mmc: fsl_esdhc_imx: use VENDORSPEC_FRC_SDCLK_ON to control card clock output""), it involve issue in mmc_switch_voltage(), because of the special design of usdhc. For FSL_USDHC, it do not implement VENDORSPEC_CKEN/PEREN/HCKEN/IPGEN, these are reserved bits(Though RM contain the definition of these bits, but actually internal IC logic do not implement, already confirm with IC team). Instead, use VENDORSPEC_FRC_SDCLK_ON to gate on/off the card clock output. Here is the definition of this bit in RM: [8] FRC_SDCLK_ON Force CLK output active Do not set this bit to 1 unless it is necessary. Also, make sure that this bit is cleared when uSDHC’s clock is about to be changed (frequency change, clock source change, or delay chain tuning). 0b - CLK active or inactive is fully controlled by the hardware. 1b - Force CLK active In default, the FRC_SDCLK_ON is 0. This means, when there is no command or data transfer on bus, hardware will gate off the card clock. But in some case, we need the card clock keep on. Take IO voltage 1.8v switch as example, after IO voltage change to 1.8v, spec require gate off the card clock for 5ms, and gate on the clock back, once detect the card clock on, then the card will draw the dat0 to high immediately. If there is not clock gate off/on behavior, some card will keep the dat0 to low level. This is the reason we fail in mmc_switch_voltage(). To fix this issue, and concern that this is only the fsl usdhc hardware design limitation, set the bit FRC_SDCLK_ON in the beginning of the wait_dat0() and clear it in the end. To make sure the 1.8v IO voltage switch process align with SD specification. For standard tuning process, usdhc specification also require the card clock keep on, so also add these behavior in fsl_esdhc_execute_tuning(). Reviewed-by: Marek Vasut Tested-by: Fabio Estevam Signed-off-by: Haibo Chen Reviewed-by: Peng Fan Reviewed-by: Jaehoon Chung --- drivers/mmc/fsl_esdhc_imx.c | 25 ++++++++++++++++++++++--- include/fsl_esdhc_imx.h | 2 ++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 697e3c641d..02208a5ade 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -827,13 +827,16 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) struct mmc *mmc = &plat->mmc; u32 irqstaten = esdhc_read32(®s->irqstaten); u32 irqsigen = esdhc_read32(®s->irqsigen); - int i, ret = -ETIMEDOUT; - u32 val, mixctrl; + int i, err, ret = -ETIMEDOUT; + u32 val, mixctrl, tmp; /* clock tuning is not needed for upto 52MHz */ if (mmc->clock <= 52000000) return 0; + /* make sure the card clock keep on */ + esdhc_setbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + /* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */ if (priv->flags & ESDHC_FLAG_STD_TUNING) { val = esdhc_read32(®s->autoc12err); @@ -893,6 +896,12 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) esdhc_stop_tuning(mmc); + /* change to default setting, let host control the card clock */ + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + err = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, tmp & PRSSTAT_SDOFF, 100); + if (err) + dev_warn(dev, "card clock not gate off as expect.\n"); + return ret; } #endif @@ -1567,14 +1576,24 @@ static int __maybe_unused fsl_esdhc_set_enhanced_strobe(struct udevice *dev) static int fsl_esdhc_wait_dat0(struct udevice *dev, int state, int timeout_us) { - int ret; + int ret, err; u32 tmp; struct fsl_esdhc_priv *priv = dev_get_priv(dev); struct fsl_esdhc *regs = priv->esdhc_regs; + /* make sure the card clock keep on */ + esdhc_setbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + ret = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, !!(tmp & PRSSTAT_DAT0) == !!state, timeout_us); + + /* change to default setting, let host control the card clock */ + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_FRC_SDCLK_ON); + err = readx_poll_timeout(esdhc_read32, ®s->prsstat, tmp, tmp & PRSSTAT_SDOFF, 100); + if (err) + dev_warn(dev, "card clock not gate off as expect.\n"); + return ret; } diff --git a/include/fsl_esdhc_imx.h b/include/fsl_esdhc_imx.h index 2153f29bef..b8efd2a166 100644 --- a/include/fsl_esdhc_imx.h +++ b/include/fsl_esdhc_imx.h @@ -37,6 +37,7 @@ #define VENDORSPEC_HCKEN 0x00001000 #define VENDORSPEC_IPGEN 0x00000800 #define VENDORSPEC_INIT 0x20007809 +#define VENDORSPEC_FRC_SDCLK_ON 0x00000100 #define IRQSTAT 0x0002e030 #define IRQSTAT_DMAE (0x10000000) @@ -94,6 +95,7 @@ #define PRSSTAT_CINS (0x00010000) #define PRSSTAT_BREN (0x00000800) #define PRSSTAT_BWEN (0x00000400) +#define PRSSTAT_SDOFF (0x00000080) #define PRSSTAT_SDSTB (0X00000008) #define PRSSTAT_DLA (0x00000004) #define PRSSTAT_CICHB (0x00000002) From 84191f73847c351019bb50728d28220d91b0aee4 Mon Sep 17 00:00:00 2001 From: Max Merchel Date: Thu, 10 Feb 2022 10:16:39 +0100 Subject: [PATCH 2/6] cmd/mmc: fix output of mmc info for e-MMC e-MMC and SD standards differ for some CID fields: - 6 Byte Name - assigned by Manufacturer (SD 5 Byte) - 1 Byte OEM - assigned by Jedec (SD 2 Byte) See e-MMC standard (JEDEC Standard No. 84-B51), 7.2.3 (OID) and 7.2.4 (PNM) Signed-off-by: Max Merchel Signed-off-by: Markus Niebel Reviewed-by: Jaehoon Chung --- cmd/mmc.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cmd/mmc.c b/cmd/mmc.c index 503dbb6199..7464f8d00c 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -22,10 +22,18 @@ static void print_mmcinfo(struct mmc *mmc) printf("Device: %s\n", mmc->cfg->name); printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); - printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); - printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, - (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, - (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + if (IS_SD(mmc)) { + printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); + printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + } else { + printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xff); + printf("Name: %c%c%c%c%c%c \n", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, + (mmc->cid[2] >> 24)); + } printf("Bus Speed: %d\n", mmc->clock); #if CONFIG_IS_ENABLED(MMC_VERBOSE) From 0f3466f52fbacce67e147b9234e6323edff26a6d Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 11 Mar 2022 19:14:07 +0100 Subject: [PATCH 3/6] mmc: xenon_sdhci: remove wait_dat0 SDHCI OP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generic SDHCI driver received support for checking the busy status by polling the DAT[0] level instead of waiting for the worst MMC switch time. Unfortunately, it appears that this does not work for Xenon controllers despite being a part of the standard SDHCI registers and the Armada 3720 datasheet itself telling that BIT(20) is useful for detecting the DAT[0] busy signal. I have tried increasing the timeout value, but I have newer managed to catch DAT_LEVEL bits change from 0 at all. This issue appears to hit most if not all SoC-s supported by Xenon driver, at least A3720, A8040 and CN9130 have non working eMMC currently. So, until a better solution is found drop the wait_dat0 OP for Xenon. I was able to only test it on A3720, but it should work for others as well. Fixes: 40e6f52454fc ("drivers: mmc: Add wait_dat0 support for sdhci driver") Signed-off-by: Robert Marko Reviewed-by: Marek Behún Reviewed-by: Jaehoon Chung Reviewed-by: Stefan Roese --- drivers/mmc/xenon_sdhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/xenon_sdhci.c b/drivers/mmc/xenon_sdhci.c index e292f2903d..2f8805096c 100644 --- a/drivers/mmc/xenon_sdhci.c +++ b/drivers/mmc/xenon_sdhci.c @@ -439,6 +439,8 @@ static const struct sdhci_ops xenon_sdhci_ops = { .set_ios_post = xenon_sdhci_set_ios_post }; +static struct dm_mmc_ops xenon_mmc_ops; + static int xenon_sdhci_probe(struct udevice *dev) { struct xenon_sdhci_plat *plat = dev_get_plat(dev); @@ -452,6 +454,9 @@ static int xenon_sdhci_probe(struct udevice *dev) host->mmc->dev = dev; upriv->mmc = host->mmc; + xenon_mmc_ops = sdhci_ops; + xenon_mmc_ops.wait_dat0 = NULL; + /* Set quirks */ host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_32BIT_DMA_ADDR; @@ -568,7 +573,7 @@ U_BOOT_DRIVER(xenon_sdhci_drv) = { .id = UCLASS_MMC, .of_match = xenon_sdhci_ids, .of_to_plat = xenon_sdhci_of_to_plat, - .ops = &sdhci_ops, + .ops = &xenon_mmc_ops, .bind = xenon_sdhci_bind, .probe = xenon_sdhci_probe, .remove = xenon_sdhci_remove, From 2a1d7c635fdede97a05d16067a936bba0a37f908 Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Tue, 15 Mar 2022 20:46:26 +0300 Subject: [PATCH 4/6] mmc: sdhci: Add HS400 Enhanced Strobe support Delegate setting the Enhanced Strobe configuration to individual drivers if they set a function for it. Return -ENOTSUPP if they do not, like what the MMC uclass does. Signed-off-by: Alper Nebi Yasak Reviewed-by: Jaehoon Chung Reviewed-by: Kever Yang --- drivers/mmc/sdhci.c | 18 ++++++++++++++++++ include/sdhci.h | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 766e4a6b0c..bf989a594f 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -513,6 +513,7 @@ void sdhci_set_uhs_timing(struct sdhci_host *host) reg |= SDHCI_CTRL_UHS_SDR104; break; case MMC_HS_400: + case MMC_HS_400_ES: reg |= SDHCI_CTRL_HS400; break; default: @@ -666,6 +667,7 @@ static int sdhci_set_ios(struct mmc *mmc) mmc->selected_mode == MMC_DDR_52 || mmc->selected_mode == MMC_HS_200 || mmc->selected_mode == MMC_HS_400 || + mmc->selected_mode == MMC_HS_400_ES || mmc->selected_mode == UHS_SDR25 || mmc->selected_mode == UHS_SDR50 || mmc->selected_mode == UHS_SDR104 || @@ -799,6 +801,19 @@ static int sdhci_wait_dat0(struct udevice *dev, int state, return -ETIMEDOUT; } +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) +static int sdhci_set_enhanced_strobe(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct sdhci_host *host = mmc->priv; + + if (host->ops && host->ops->set_enhanced_strobe) + return host->ops->set_enhanced_strobe(host); + + return -ENOTSUPP; +} +#endif + const struct dm_mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, @@ -808,6 +823,9 @@ const struct dm_mmc_ops sdhci_ops = { .execute_tuning = sdhci_execute_tuning, #endif .wait_dat0 = sdhci_wait_dat0, +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) + .set_enhanced_strobe = sdhci_set_enhanced_strobe, +#endif }; #else static const struct mmc_ops sdhci_ops = { diff --git a/include/sdhci.h b/include/sdhci.h index c8d69f5a63..88f1917480 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -272,6 +272,18 @@ struct sdhci_ops { int (*platform_execute_tuning)(struct mmc *host, u8 opcode); int (*set_delay)(struct sdhci_host *host); int (*deferred_probe)(struct sdhci_host *host); + + /** + * set_enhanced_strobe() - Set HS400 Enhanced Strobe config + * + * This is called after setting the card speed and mode to + * HS400 ES, and should set any host-specific configuration + * necessary for it. + * + * @host: SDHCI host structure + * Return: 0 if successful, -ve on error + */ + int (*set_enhanced_strobe)(struct sdhci_host *host); }; #define ADMA_MAX_LEN 65532 From c35af783172563f6a8b55d45ba244cb2d2f09cce Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Tue, 15 Mar 2022 20:46:27 +0300 Subject: [PATCH 5/6] rockchip: sdhci: Add HS400 Enhanced Strobe support for RK3399 On RK3399, a register bit must be set to enable Enhanced Strobe. Let the Rockchip SDHCI driver set it when Enhanced Strobe configuration is requested. However, having it set makes the lower-speed modes stop working and makes reinitialization fail, so let it be unset as needed in set_control_reg(). This is mostly ported from Linux's Arasan SDHCI driver which happens to be the underlying IP. (drivers/mmc/host/sdhci-of-arasan.c in Linux tree). Signed-off-by: Alper Nebi Yasak Reviewed-by: Jaehoon Chung Reviewed-by: Kever Yang --- drivers/mmc/rockchip_sdhci.c | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index b91df05de4..f4d5a59036 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -42,6 +42,9 @@ ((((x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ PHYCTRL_DLLRDY_DONE) +#define ARASAN_VENDOR_REGISTER 0x78 +#define ARASAN_VENDOR_ENHANCED_STROBE BIT(0) + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1) @@ -117,6 +120,19 @@ struct sdhci_data { * Return: 0 if successful, -ve on error */ int (*set_ios_post)(struct sdhci_host *host); + + /** + * set_enhanced_strobe() - Set HS400 Enhanced Strobe config + * + * This is the set_enhanced_strobe() SDHCI operation that should + * be used for the hardware this driver data is associated with. + * Normally, this is used to set any host-specific configuration + * necessary for HS400 ES. + * + * @host: SDHCI host structure + * Return: 0 if successful, -ve on error + */ + int (*set_enhanced_strobe)(struct sdhci_host *host); }; static int rk3399_emmc_phy_init(struct udevice *dev) @@ -206,6 +222,21 @@ static int rk3399_emmc_get_phy(struct udevice *dev) return 0; } +static int rk3399_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct mmc *mmc = host->mmc; + u32 vendor; + + vendor = sdhci_readl(host, ARASAN_VENDOR_REGISTER); + if (mmc->selected_mode == MMC_HS_400_ES) + vendor |= ARASAN_VENDOR_ENHANCED_STROBE; + else + vendor &= ~ARASAN_VENDOR_ENHANCED_STROBE; + sdhci_writel(host, vendor, ARASAN_VENDOR_REGISTER); + + return 0; +} + static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) { struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); @@ -217,6 +248,15 @@ static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) rk3399_emmc_phy_power_off(priv->phy); sdhci_set_control_reg(host); + + /* + * Reinitializing the device tries to set it to lower-speed modes + * first, which fails if the Enhanced Strobe bit is set, making + * the device impossible to use. Set the correct value here to + * let reinitialization attempts succeed. + */ + if (CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)) + rk3399_sdhci_set_enhanced_strobe(host); }; static int rk3399_sdhci_set_ios_post(struct sdhci_host *host) @@ -409,10 +449,22 @@ static int rockchip_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) return ret; } +static int rockchip_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); + + if (data->set_enhanced_strobe) + return data->set_enhanced_strobe(host); + + return -ENOTSUPP; +} + static struct sdhci_ops rockchip_sdhci_ops = { .set_ios_post = rockchip_sdhci_set_ios_post, .platform_execute_tuning = &rockchip_sdhci_execute_tuning, .set_control_reg = rockchip_sdhci_set_control_reg, + .set_enhanced_strobe = rockchip_sdhci_set_enhanced_strobe, }; static int rockchip_sdhci_probe(struct udevice *dev) @@ -495,6 +547,7 @@ static const struct sdhci_data rk3399_data = { .emmc_phy_init = rk3399_emmc_phy_init, .set_control_reg = rk3399_sdhci_set_control_reg, .set_ios_post = rk3399_sdhci_set_ios_post, + .set_enhanced_strobe = rk3399_sdhci_set_enhanced_strobe, }; static const struct sdhci_data rk3568_data = { From c48021d184097ea4a1bb6bab8c24653de2477fde Mon Sep 17 00:00:00 2001 From: Alper Nebi Yasak Date: Tue, 15 Mar 2022 20:46:28 +0300 Subject: [PATCH 6/6] rockchip: sdhci: Add HS400 Enhanced Strobe support for RK3568 On RK3568, a register bit must be set to enable Enhanced Strobe. However, it appears that the address of this register may differ from vendor to vendor and should be read from the underlying MMC IP. Let the Rockchip SDHCI driver read this address and set the relevant bit when Enhanced Strobe configuration is requested. The IP uses a custom mode select value (0x7) for HS400, use that instead of the common but non-standard SDHCI_CTRL_HS400 value (0x5). Also add some necessary DLL_STRBIN and DLL_TXCLK configuration for HS400. Additionally, a bit signifying that the connected hardware is an eMMC chip must be set to enable Data Strobe for HS400 and HS400ES modes. Also make the driver set this bit as appropriate. This is partly ported from Linux's Synopsys DWC MSHC driver which happens to be the underlying IP. (drivers/mmc/host/sdhci-of-dwcmshc.c in Linux tree). Co-developed-by: Yifeng Zhao Signed-off-by: Yifeng Zhao Signed-off-by: Alper Nebi Yasak Reviewed-by: Kever Yang --- drivers/mmc/rockchip_sdhci.c | 64 ++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index f4d5a59036..f3f9d83ba3 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -22,6 +22,8 @@ #include #include +/* DWCMSHC specific Mode Select value */ +#define DWCMSHC_CTRL_HS400 0x7 /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 400000 #define KHz (1000) @@ -45,6 +47,14 @@ #define ARASAN_VENDOR_REGISTER 0x78 #define ARASAN_VENDOR_ENHANCED_STROBE BIT(0) +/* DWC IP vendor area 1 pointer */ +#define DWCMSHC_P_VENDOR_AREA1 0xe8 +#define DWCMSHC_AREA1_MASK GENMASK(11, 0) +/* Offset inside the vendor area 1 */ +#define DWCMSHC_EMMC_CONTROL 0x2c +#define DWCMSHC_CARD_IS_EMMC BIT(0) +#define DWCMSHC_ENHANCED_STROBE BIT(8) + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1) @@ -60,8 +70,14 @@ #define DWCMSHC_EMMC_DLL_INC_VALUE 2 #define DWCMSHC_EMMC_DLL_INC 8 #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) -#define DLL_TXCLK_TAPNUM_DEFAULT 0x10 -#define DLL_STRBIN_TAPNUM_DEFAULT 0x3 +#define DLL_TXCLK_TAPNUM_DEFAULT 0xA + +#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) +#define DLL_STRBIN_DELAY_NUM_SEL BIT(26) +#define DLL_STRBIN_DELAY_NUM_OFFSET 16 +#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16 + #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) #define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9) @@ -327,7 +343,8 @@ static int rk3568_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clo sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); extra = DWCMSHC_EMMC_DLL_DLYENA | - DLL_STRBIN_TAPNUM_DEFAULT; + DLL_STRBIN_TAPNUM_DEFAULT | + DLL_STRBIN_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } else { /* reset the clock phase when the frequency is lower than 100MHz */ @@ -335,7 +352,15 @@ static int rk3568_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clo extra = DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN); + /* + * Before switching to hs400es mode, the driver will enable + * enhanced strobe first. PHY needs to configure the parameters + * of enhanced strobe first. + */ + extra = DWCMSHC_EMMC_DLL_DLYENA | + DLL_STRBIN_DELAY_NUM_SEL | + DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } return 0; @@ -346,11 +371,30 @@ static int rk3568_emmc_get_phy(struct udevice *dev) return 0; } +static int rk3568_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct mmc *mmc = host->mmc; + u32 vendor; + int reg; + + reg = (sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK) + + DWCMSHC_EMMC_CONTROL; + + vendor = sdhci_readl(host, reg); + if (mmc->selected_mode == MMC_HS_400_ES) + vendor |= DWCMSHC_ENHANCED_STROBE; + else + vendor &= ~DWCMSHC_ENHANCED_STROBE; + sdhci_writel(host, vendor, reg); + + return 0; +} + static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) { struct mmc *mmc = host->mmc; uint clock = mmc->tran_speed; - u32 reg; + u32 reg, vendor_reg; if (!clock) clock = mmc->clock; @@ -360,8 +404,15 @@ static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) if (mmc->selected_mode == MMC_HS_400 || mmc->selected_mode == MMC_HS_400_ES) { reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); reg &= ~SDHCI_CTRL_UHS_MASK; - reg |= SDHCI_CTRL_HS400; + reg |= DWCMSHC_CTRL_HS400; sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); + + vendor_reg = (sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK) + + DWCMSHC_EMMC_CONTROL; + /* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */ + reg = sdhci_readw(host, vendor_reg); + reg |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, reg, vendor_reg); } else { sdhci_set_uhs_timing(host); } @@ -554,6 +605,7 @@ static const struct sdhci_data rk3568_data = { .get_phy = rk3568_emmc_get_phy, .emmc_phy_init = rk3568_emmc_phy_init, .set_ios_post = rk3568_sdhci_set_ios_post, + .set_enhanced_strobe = rk3568_sdhci_set_enhanced_strobe, }; static const struct udevice_id sdhci_ids[] = {