2015-08-05 10:35:55

by Chen Bough

[permalink] [raw]
Subject: [PATCH v4 0/6] mmc: imx: a few fixes and new feature

Changes for v4:
-Call esdhc_set_strobe_dll() only when device clock is over 100MHz in HS400 mode.
-Add detail description of tuning-step.
-Change to default watermark level and burst length for all imx SoC, not only imx7d.

Haibo Chen (6):
mmc: sdhci-esdhc-imx: add imx7d support and support HS400
mmc: sdhci-esdhc-imx: add tuning-step seting support
mmc: sdhci-esdhc-imx: add compatible string in bingding doc
ARM: dts: imx7d-sdb: add eMMC5.0 support
mmc: sdhci-esdhc-imx: change default watermark level and burst length
mmc: sdhci-esdhc-imx: set back the burst_length_enable bit to 1

.../devicetree/bindings/mmc/fsl-imx-esdhc.txt | 6 ++
arch/arm/boot/dts/imx7d-sdb.dts | 13 +++
drivers/mmc/host/sdhci-esdhc-imx.c | 109 ++++++++++++++++++++-
include/linux/platform_data/mmc-esdhc-imx.h | 1 +
4 files changed, 128 insertions(+), 1 deletion(-)

--
1.9.1


2015-08-05 10:36:05

by Chen Bough

[permalink] [raw]
Subject: [PATCH v4 1/6] mmc: sdhci-esdhc-imx: add imx7d support and support HS400

The imx7d usdhc is derived from imx6sx, the difference is that
imx7d support HS400.

So introduce a new compatible string for imx7d and add HS400
support for imx7d usdhc.

Signed-off-by: Haibo Chen <[email protected]>
---
drivers/mmc/host/sdhci-esdhc-imx.c | 81 ++++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)

diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index c6b9f64..48f009c 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -44,6 +44,7 @@
#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
+#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
/* Bits 3 and 6 are not SDHCI standard definitions */
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
/* Tuning bits */
@@ -60,6 +61,16 @@
#define ESDHC_TUNE_CTRL_MIN 0
#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)

+/* strobe dll register */
+#define ESDHC_STROBE_DLL_CTRL 0x70
+#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
+#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1)
+#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3
+
+#define ESDHC_STROBE_DLL_STATUS 0x74
+#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
+#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1
+
#define ESDHC_TUNING_CTRL 0xcc
#define ESDHC_STD_TUNING_EN (1 << 24)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
@@ -120,6 +131,11 @@
#define ESDHC_FLAG_ERR004536 BIT(7)
/* The IP supports HS200 mode */
#define ESDHC_FLAG_HS200 BIT(8)
+/* The IP supports HS400 mode */
+#define ESDHC_FLAG_HS400 BIT(9)
+
+/* A higher clock ferquency than this rate requires strobell dll control */
+#define ESDHC_STROBE_DLL_CLK_FREQ 100000000

struct esdhc_soc_data {
u32 flags;
@@ -156,6 +172,12 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
};

+static struct esdhc_soc_data usdhc_imx7d_data = {
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
+ | ESDHC_FLAG_HS400,
+};
+
struct pltfm_imx_data {
u32 scratchpad;
struct pinctrl *pinctrl;
@@ -199,6 +221,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
+ { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
@@ -274,6 +297,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
| SDHCI_SUPPORT_SDR50
| SDHCI_USE_SDR50_TUNING;
+
+ if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
+ val |= SDHCI_SUPPORT_HS400;
}
}

@@ -774,6 +800,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
break;
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
+ case MMC_TIMING_MMC_HS400:
pinctrl = imx_data->pins_200mhz;
break;
default:
@@ -784,6 +811,44 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
return pinctrl_select_state(imx_data->pinctrl, pinctrl);
}

+/*
+ * For HS400 eMMC, there is a data_strobe line, this signal is generated
+ * by the device and used for data output and CRC status response output
+ * in HS400 mode. The frequency of this signal follows the frequency of
+ * CLK generated by host. Host receive the data which is aligned to the
+ * edge of data_strobe line. Due to the time delay between CLK line and
+ * data_strobe line, if the delay time is larger than one clock cycle,
+ * then CLK and data_strobe line will misaligned, read error shows up.
+ * So when the CLK is higher than 100MHz, each clock cycle is short enough,
+ * host should config the delay target.
+ */
+static void esdhc_set_strobe_dll(struct sdhci_host *host)
+{
+ u32 v;
+
+ if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
+ /* force a reset on strobe dll */
+ writel(ESDHC_STROBE_DLL_CTRL_RESET,
+ host->ioaddr + ESDHC_STROBE_DLL_CTRL);
+ /*
+ * enable strobe dll ctrl and adjust the delay target
+ * for the uSDHC loopback read clock
+ */
+ v = ESDHC_STROBE_DLL_CTRL_ENABLE |
+ (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
+ writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
+ /* wait 1us to make sure strobe dll status register stable */
+ udelay(1);
+ v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
+ if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
+ dev_warn(mmc_dev(host->mmc),
+ "warning! HS400 strobe DLL status REF not lock!\n");
+ if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
+ dev_warn(mmc_dev(host->mmc),
+ "warning! HS400 strobe DLL status SLV not lock!\n");
+ }
+}
+
static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -795,7 +860,13 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
case MMC_TIMING_UHS_SDR25:
case MMC_TIMING_UHS_SDR50:
case MMC_TIMING_UHS_SDR104:
+ break;
case MMC_TIMING_MMC_HS200:
+ /* disable ddr mode and disable HS400 mode */
+ writel(readl(host->ioaddr + ESDHC_MIX_CTRL) &
+ ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN),
+ host->ioaddr + ESDHC_MIX_CTRL);
+ imx_data->is_ddr = 0;
break;
case MMC_TIMING_UHS_DDR50:
case MMC_TIMING_MMC_DDR52:
@@ -813,6 +884,13 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
writel(v, host->ioaddr + ESDHC_DLL_CTRL);
}
break;
+ case MMC_TIMING_MMC_HS400:
+ writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
+ ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN,
+ host->ioaddr + ESDHC_MIX_CTRL);
+ imx_data->is_ddr = 1;
+ esdhc_set_strobe_dll(host);
+ break;
}

esdhc_change_pinstate(host, timing);
@@ -1100,6 +1178,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;

+ if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
+ host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
+
if (of_id)
err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
else
--
1.9.1

2015-08-05 10:38:06

by Chen Bough

[permalink] [raw]
Subject: [PATCH v4 2/6] mmc: sdhci-esdhc-imx: add tuning-step seting support

tuning-step is the delay cell steps in tuning procedure. The default value
of tuning-step is 1. Some boards or cards need another value to pass the
tuning procedure. For example, imx7d-sdb board need the tuning-step value
as 2, otherwise it can't pass the tuning procedure.

So this patch add the tuning-step setting in driver, so that user can set
the tuning-step value in dts.

Signed-off-by: Haibo Chen <[email protected]>
---
drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++++
include/linux/platform_data/mmc-esdhc-imx.h | 1 +
2 files changed, 10 insertions(+)

diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 48f009c..803d24f 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -75,6 +75,7 @@
#define ESDHC_STD_TUNING_EN (1 << 24)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
#define ESDHC_TUNING_START_TAP 0x1
+#define ESDHC_TUNING_STEP_SHIFT 16

/* pinctrl state */
#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
@@ -474,6 +475,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ u32 tuning_ctrl;
if (val & SDHCI_CTRL_TUNED_CLK) {
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
} else {
@@ -484,6 +486,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
if (val & SDHCI_CTRL_EXEC_TUNING) {
v |= ESDHC_MIX_CTRL_EXE_TUNE;
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
+ tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+ tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP;
+ if (imx_data->boarddata.tuning_step)
+ tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT;
+ writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
} else {
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
}
@@ -964,6 +971,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
if (gpio_is_valid(boarddata->wp_gpio))
boarddata->wp_type = ESDHC_WP_GPIO;

+ of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
+
if (of_find_property(np, "no-1-8-v", NULL))
boarddata->support_vsel = false;
else
diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h
index e1571ef..95ccab3 100644
--- a/include/linux/platform_data/mmc-esdhc-imx.h
+++ b/include/linux/platform_data/mmc-esdhc-imx.h
@@ -45,5 +45,6 @@ struct esdhc_platform_data {
int max_bus_width;
bool support_vsel;
unsigned int delay_line;
+ unsigned int tuning_step; /* The delay cell steps in tuning procedure */
};
#endif /* __ASM_ARCH_IMX_ESDHC_H */
--
1.9.1

2015-08-05 10:36:17

by Chen Bough

[permalink] [raw]
Subject: [PATCH v4 3/6] mmc: sdhci-esdhc-imx: add compatible string in bingding doc

Add a required property "fsl,imx7d-usdhc" in binding doc.
Add an optional property "fsl,tuning-step" in binding doc.

Signed-off-by: Haibo Chen <[email protected]>
---
Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
index 211e778..dca56d6 100644
--- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
+++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
@@ -15,6 +15,7 @@ Required properties:
"fsl,imx6q-usdhc"
"fsl,imx6sl-usdhc"
"fsl,imx6sx-usdhc"
+ "fsl,imx7d-usdhc"

Optional properties:
- fsl,wp-controller : Indicate to use controller internal write protection
@@ -27,6 +28,11 @@ Optional properties:
transparent level shifters on the outputs of the controller. Two cells are
required, first cell specifies minimum slot voltage (mV), second cell
specifies maximum slot voltage (mV). Several ranges could be specified.
+- fsl,tuning-step: Specify the increasing delay cell steps in tuning procedure.
+ The uSDHC use one delay cell as default increasing step to do tuning process.
+ This property allows user to change the tuning step to more than one delay
+ cells which is useful for some special boards or cards when the default
+ tuning step can't find the proper delay window within limited tuning retries.

Examples:

--
1.9.1

2015-08-05 10:36:59

by Chen Bough

[permalink] [raw]
Subject: [PATCH v4 4/6] ARM: dts: imx7d-sdb: add eMMC5.0 support

imx7d-sdb board has a eMMC5.0 on usdhc3. This eMMC support HS400.
This patch add usdhc3 support for HS400

Signed-off-by: Haibo Chen <[email protected]>
---
arch/arm/boot/dts/imx7d-sdb.dts | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts
index fdd1d7c..8059458 100644
--- a/arch/arm/boot/dts/imx7d-sdb.dts
+++ b/arch/arm/boot/dts/imx7d-sdb.dts
@@ -241,6 +241,19 @@
status = "okay";
};

+&usdhc3 {
+ pinctrl-names = "default", "state_100mhz", "state_200mhz";
+ pinctrl-0 = <&pinctrl_usdhc3>;
+ pinctrl-1 = <&pinctrl_usdhc3_100mhz>;
+ pinctrl-2 = <&pinctrl_usdhc3_200mhz>;
+ assigned-clocks = <&clks IMX7D_USDHC3_ROOT_CLK>;
+ assigned-clock-rates = <400000000>;
+ bus-width = <8>;
+ fsl,tuning-step = <2>;
+ non-removable;
+ status = "okay";
+};
+
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
--
1.9.1

2015-08-05 10:36:25

by Chen Bough

[permalink] [raw]
Subject: [PATCH v4 5/6] mmc: sdhci-esdhc-imx: change default watermark level and burst length

By default, for all imx SoC types, the watermark level is 16, and the
burst length is 8. But if the SDIO/SD/MMC I/O speed is fast enough,
this default watermark level and burst length will be the performance
bottleneck.

For example, i.MX7D support eMMC HS400 mode, this mode can run in 8 bit,
200MHZ DDR mode. So the I/O speed improve a lot compare to SD3.0.
The default burst length is 8, if we don't change this value, in
HS400 mode, when we do eMMC read operation, we can find that the
clock signal will stop for a period of time. This means the speed
of data moving on AHB bus is slower than I/O speed. So we should
improve the speed of data moving on AHB bus.

This patch set the default burst length as 16, and set the default
watermark level as 64. The test result is the clock signal has
no stop during the eMMC HS400 operation.

Signed-off-by: Haibo Chen <[email protected]>
---
drivers/mmc/host/sdhci-esdhc-imx.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 803d24f..97aa944 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -1160,7 +1160,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
* to something insane. Change it back here.
*/
if (esdhc_is_usdhc(imx_data)) {
- writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
+ writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL);
+
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
host->mmc->caps |= MMC_CAP_1_8V_DDR;

--
1.9.1

2015-08-05 10:36:30

by Chen Bough

[permalink] [raw]
Subject: [PATCH v4 6/6] mmc: sdhci-esdhc-imx: set back the burst_length_enable bit to 1

Currently we find that if a usdhc is choosed to boot system, then ROM
code will set the burst length enable bit of this usdhc as 0.

This will make performance drop a lot if this usdhc's burst length is
configed. So this patch set back the burst_length_enable bit as 1,
which is the default value, and means burst length is enabled for INCR.

Signed-off-by: Haibo Chen <[email protected]>
---
drivers/mmc/host/sdhci-esdhc-imx.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 97aa944..3334762 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -32,6 +32,7 @@
#include "sdhci-esdhc.h"

#define ESDHC_CTRL_D3CD 0x08
+#define ESDHC_BURST_LEN_EN_INCR (1 << 27)
/* VENDOR SPEC register */
#define ESDHC_VENDOR_SPEC 0xc0
#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
@@ -1165,6 +1166,21 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
host->mmc->caps |= MMC_CAP_1_8V_DDR;

+ /*
+ * ROM code will change the bit burst_length_enable setting
+ * to zero if this usdhc is choosed to boot system. Change
+ * it back here, otherwise it will impact the performance a
+ * lot. This bit is used to enable/disable the burst length
+ * for the external AHB2AXI bridge, it's usefully especially
+ * for INCR transfer because without burst length indicator,
+ * the AHB2AXI bridge does not know the burst length in
+ * advance. And without burst length indicator, AHB INCR
+ * transfer can only be converted to singles on the AXI side.
+ */
+ writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
+ | ESDHC_BURST_LEN_EN_INCR,
+ host->ioaddr + SDHCI_HOST_CONTROL);
+
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;

--
1.9.1

2015-08-07 07:48:35

by Dong Aisheng

[permalink] [raw]
Subject: Re: [PATCH v4 1/6] mmc: sdhci-esdhc-imx: add imx7d support and support HS400

On Wed, Aug 05, 2015 at 06:38:37PM +0800, Haibo Chen wrote:
> The imx7d usdhc is derived from imx6sx, the difference is that
> imx7d support HS400.
>
> So introduce a new compatible string for imx7d and add HS400
> support for imx7d usdhc.
>
> Signed-off-by: Haibo Chen <[email protected]>
> ---
> drivers/mmc/host/sdhci-esdhc-imx.c | 81 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index c6b9f64..48f009c 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -44,6 +44,7 @@
> #define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
> #define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
> #define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
> +#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
> /* Bits 3 and 6 are not SDHCI standard definitions */
> #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
> /* Tuning bits */
> @@ -60,6 +61,16 @@
> #define ESDHC_TUNE_CTRL_MIN 0
> #define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
>
> +/* strobe dll register */
> +#define ESDHC_STROBE_DLL_CTRL 0x70
> +#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
> +#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1)
> +#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3
> +
> +#define ESDHC_STROBE_DLL_STATUS 0x74
> +#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
> +#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1
> +
> #define ESDHC_TUNING_CTRL 0xcc
> #define ESDHC_STD_TUNING_EN (1 << 24)
> /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
> @@ -120,6 +131,11 @@
> #define ESDHC_FLAG_ERR004536 BIT(7)
> /* The IP supports HS200 mode */
> #define ESDHC_FLAG_HS200 BIT(8)
> +/* The IP supports HS400 mode */
> +#define ESDHC_FLAG_HS400 BIT(9)
> +
> +/* A higher clock ferquency than this rate requires strobell dll control */
> +#define ESDHC_STROBE_DLL_CLK_FREQ 100000000
>
> struct esdhc_soc_data {
> u32 flags;
> @@ -156,6 +172,12 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
> | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
> };
>
> +static struct esdhc_soc_data usdhc_imx7d_data = {
> + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
> + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
> + | ESDHC_FLAG_HS400,
> +};
> +
> struct pltfm_imx_data {
> u32 scratchpad;
> struct pinctrl *pinctrl;
> @@ -199,6 +221,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
> { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
> { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
> { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
> + { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
> { /* sentinel */ }
> };
> MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
> @@ -274,6 +297,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
> val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
> | SDHCI_SUPPORT_SDR50
> | SDHCI_USE_SDR50_TUNING;
> +
> + if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
> + val |= SDHCI_SUPPORT_HS400;
> }
> }
>
> @@ -774,6 +800,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
> break;
> case MMC_TIMING_UHS_SDR104:
> case MMC_TIMING_MMC_HS200:
> + case MMC_TIMING_MMC_HS400:
> pinctrl = imx_data->pins_200mhz;
> break;
> default:
> @@ -784,6 +811,44 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
> return pinctrl_select_state(imx_data->pinctrl, pinctrl);
> }
>
> +/*
> + * For HS400 eMMC, there is a data_strobe line, this signal is generated
> + * by the device and used for data output and CRC status response output
> + * in HS400 mode. The frequency of this signal follows the frequency of
> + * CLK generated by host. Host receive the data which is aligned to the
> + * edge of data_strobe line. Due to the time delay between CLK line and
> + * data_strobe line, if the delay time is larger than one clock cycle,
> + * then CLK and data_strobe line will misaligned, read error shows up.
> + * So when the CLK is higher than 100MHz, each clock cycle is short enough,
> + * host should config the delay target.
> + */
> +static void esdhc_set_strobe_dll(struct sdhci_host *host)
> +{
> + u32 v;
> +
> + if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
> + /* force a reset on strobe dll */
> + writel(ESDHC_STROBE_DLL_CTRL_RESET,
> + host->ioaddr + ESDHC_STROBE_DLL_CTRL);
> + /*
> + * enable strobe dll ctrl and adjust the delay target
> + * for the uSDHC loopback read clock
> + */
> + v = ESDHC_STROBE_DLL_CTRL_ENABLE |
> + (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
> + writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
> + /* wait 1us to make sure strobe dll status register stable */
> + udelay(1);
> + v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
> + if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
> + dev_warn(mmc_dev(host->mmc),
> + "warning! HS400 strobe DLL status REF not lock!\n");
> + if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
> + dev_warn(mmc_dev(host->mmc),
> + "warning! HS400 strobe DLL status SLV not lock!\n");
> + }
> +}
> +
> static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
> {
> struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> @@ -795,7 +860,13 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
> case MMC_TIMING_UHS_SDR25:
> case MMC_TIMING_UHS_SDR50:
> case MMC_TIMING_UHS_SDR104:
> + break;
> case MMC_TIMING_MMC_HS200:
> + /* disable ddr mode and disable HS400 mode */
> + writel(readl(host->ioaddr + ESDHC_MIX_CTRL) &
> + ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN),
> + host->ioaddr + ESDHC_MIX_CTRL);
> + imx_data->is_ddr = 0;

Can you please explain why need above code?
Any issues if not do it?

Regards
Dong Aisheng

> break;
> case MMC_TIMING_UHS_DDR50:
> case MMC_TIMING_MMC_DDR52:
> @@ -813,6 +884,13 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
> writel(v, host->ioaddr + ESDHC_DLL_CTRL);
> }
> break;
> + case MMC_TIMING_MMC_HS400:
> + writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
> + ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN,
> + host->ioaddr + ESDHC_MIX_CTRL);
> + imx_data->is_ddr = 1;
> + esdhc_set_strobe_dll(host);
> + break;
> }
>
> esdhc_change_pinstate(host, timing);
> @@ -1100,6 +1178,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
> if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
> host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
>
> + if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
> + host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
> +
> if (of_id)
> err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
> else
> --
> 1.9.1
>

2015-08-07 07:49:57

by Dong Aisheng

[permalink] [raw]
Subject: Re: [PATCH v4 2/6] mmc: sdhci-esdhc-imx: add tuning-step seting support

On Wed, Aug 05, 2015 at 06:38:38PM +0800, Haibo Chen wrote:
> tuning-step is the delay cell steps in tuning procedure. The default value
> of tuning-step is 1. Some boards or cards need another value to pass the
> tuning procedure. For example, imx7d-sdb board need the tuning-step value
> as 2, otherwise it can't pass the tuning procedure.
>
> So this patch add the tuning-step setting in driver, so that user can set
> the tuning-step value in dts.
>
> Signed-off-by: Haibo Chen <[email protected]>
> ---
> drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++++
> include/linux/platform_data/mmc-esdhc-imx.h | 1 +
> 2 files changed, 10 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index 48f009c..803d24f 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -75,6 +75,7 @@
> #define ESDHC_STD_TUNING_EN (1 << 24)
> /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
> #define ESDHC_TUNING_START_TAP 0x1
> +#define ESDHC_TUNING_STEP_SHIFT 16
>
> /* pinctrl state */
> #define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
> @@ -474,6 +475,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
> } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
> u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
> u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + u32 tuning_ctrl;
> if (val & SDHCI_CTRL_TUNED_CLK) {
> v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
> } else {
> @@ -484,6 +486,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
> if (val & SDHCI_CTRL_EXEC_TUNING) {
> v |= ESDHC_MIX_CTRL_EXE_TUNE;
> m |= ESDHC_MIX_CTRL_FBCLK_SEL;
> + tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
> + tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP;
> + if (imx_data->boarddata.tuning_step)
> + tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT;
> + writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);

Is there a code indent issue here?
Otherwise, the patch looks good to me.

Regards
Dong Aisheng

> } else {
> v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
> }
> @@ -964,6 +971,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
> if (gpio_is_valid(boarddata->wp_gpio))
> boarddata->wp_type = ESDHC_WP_GPIO;
>
> + of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
> +
> if (of_find_property(np, "no-1-8-v", NULL))
> boarddata->support_vsel = false;
> else
> diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h
> index e1571ef..95ccab3 100644
> --- a/include/linux/platform_data/mmc-esdhc-imx.h
> +++ b/include/linux/platform_data/mmc-esdhc-imx.h
> @@ -45,5 +45,6 @@ struct esdhc_platform_data {
> int max_bus_width;
> bool support_vsel;
> unsigned int delay_line;
> + unsigned int tuning_step; /* The delay cell steps in tuning procedure */
> };
> #endif /* __ASM_ARCH_IMX_ESDHC_H */
> --
> 1.9.1
>

2015-08-07 08:00:32

by Dong Aisheng

[permalink] [raw]
Subject: Re: [PATCH v4 6/6] mmc: sdhci-esdhc-imx: set back the burst_length_enable bit to 1

On Wed, Aug 05, 2015 at 06:38:42PM +0800, Haibo Chen wrote:
> Currently we find that if a usdhc is choosed to boot system, then ROM
> code will set the burst length enable bit of this usdhc as 0.
>
> This will make performance drop a lot if this usdhc's burst length is
> configed. So this patch set back the burst_length_enable bit as 1,
> which is the default value, and means burst length is enabled for INCR.
>

This patch should be put before patch 5 to avoid function break, right?
[PATCH v4 5/6] mmc: sdhci-esdhc-imx: change default watermark level and burst length

Other than that, this patch looks good to me.

Regards
Dong Aisheng

> Signed-off-by: Haibo Chen <[email protected]>
> ---
> drivers/mmc/host/sdhci-esdhc-imx.c | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index 97aa944..3334762 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -32,6 +32,7 @@
> #include "sdhci-esdhc.h"
>
> #define ESDHC_CTRL_D3CD 0x08
> +#define ESDHC_BURST_LEN_EN_INCR (1 << 27)
> /* VENDOR SPEC register */
> #define ESDHC_VENDOR_SPEC 0xc0
> #define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
> @@ -1165,6 +1166,21 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
> host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
> host->mmc->caps |= MMC_CAP_1_8V_DDR;
>
> + /*
> + * ROM code will change the bit burst_length_enable setting
> + * to zero if this usdhc is choosed to boot system. Change
> + * it back here, otherwise it will impact the performance a
> + * lot. This bit is used to enable/disable the burst length
> + * for the external AHB2AXI bridge, it's usefully especially
> + * for INCR transfer because without burst length indicator,
> + * the AHB2AXI bridge does not know the burst length in
> + * advance. And without burst length indicator, AHB INCR
> + * transfer can only be converted to singles on the AXI side.
> + */
> + writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
> + | ESDHC_BURST_LEN_EN_INCR,
> + host->ioaddr + SDHCI_HOST_CONTROL);
> +
> if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
> host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
>
> --
> 1.9.1
>

2015-08-07 11:29:19

by Dong Aisheng

[permalink] [raw]
Subject: Re: [PATCH v4 1/6] mmc: sdhci-esdhc-imx: add imx7d support and support HS400

On Fri, Aug 07, 2015 at 05:53:01PM +0800, Chen Haibo-B51421 wrote:
>
>
> > -----Original Message-----
> > From: Dong Aisheng [mailto:[email protected]]
> > Sent: Friday, August 07, 2015 3:39 PM
> > To: Chen Haibo-B51421
> > Cc: [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]; Estevam Fabio-R49496; Dong
> > Aisheng-B29396; [email protected]; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [PATCH v4 1/6] mmc: sdhci-esdhc-imx: add imx7d support and
> > support HS400
> >
> > On Wed, Aug 05, 2015 at 06:38:37PM +0800, Haibo Chen wrote:
> > > The imx7d usdhc is derived from imx6sx, the difference is that imx7d
> > > support HS400.
> > >
> > > So introduce a new compatible string for imx7d and add HS400 support
> > > for imx7d usdhc.
> > >
> > > Signed-off-by: Haibo Chen <[email protected]>
> > > ---
> > > drivers/mmc/host/sdhci-esdhc-imx.c | 81
> > > ++++++++++++++++++++++++++++++++++++++
> > > 1 file changed, 81 insertions(+)
> > >
> > > diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c
> > > b/drivers/mmc/host/sdhci-esdhc-imx.c
> > > index c6b9f64..48f009c 100644
> > > --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> > > +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> > > @@ -44,6 +44,7 @@
> > > #define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
> > > #define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
> > > #define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
> > > +#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
> > > /* Bits 3 and 6 are not SDHCI standard definitions */
> > > #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
> > > /* Tuning bits */
> > > @@ -60,6 +61,16 @@
> > > #define ESDHC_TUNE_CTRL_MIN 0
> > > #define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
> > >
> > > +/* strobe dll register */
> > > +#define ESDHC_STROBE_DLL_CTRL 0x70
> > > +#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
> > > +#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1)
> > > +#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3
> > > +
> > > +#define ESDHC_STROBE_DLL_STATUS 0x74
> > > +#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
> > > +#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1
> > > +
> > > #define ESDHC_TUNING_CTRL 0xcc
> > > #define ESDHC_STD_TUNING_EN (1 << 24)
> > > /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ @@
> > > -120,6 +131,11 @@
> > > #define ESDHC_FLAG_ERR004536 BIT(7)
> > > /* The IP supports HS200 mode */
> > > #define ESDHC_FLAG_HS200 BIT(8)
> > > +/* The IP supports HS400 mode */
> > > +#define ESDHC_FLAG_HS400 BIT(9)
> > > +
> > > +/* A higher clock ferquency than this rate requires strobell dll
> > control */
> > > +#define ESDHC_STROBE_DLL_CLK_FREQ 100000000
> > >
> > > struct esdhc_soc_data {
> > > u32 flags;
> > > @@ -156,6 +172,12 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
> > > | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, };
> > >
> > > +static struct esdhc_soc_data usdhc_imx7d_data = {
> > > + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
> > > + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
> > > + | ESDHC_FLAG_HS400,
> > > +};
> > > +
> > > struct pltfm_imx_data {
> > > u32 scratchpad;
> > > struct pinctrl *pinctrl;
> > > @@ -199,6 +221,7 @@ static const struct of_device_id imx_esdhc_dt_ids[]
> > = {
> > > { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
> > > { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
> > > { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
> > > + { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
> > > { /* sentinel */ }
> > > };
> > > MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); @@ -274,6 +297,9 @@ static
> > > u32 esdhc_readl_le(struct sdhci_host *host, int reg)
> > > val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
> > > | SDHCI_SUPPORT_SDR50
> > > | SDHCI_USE_SDR50_TUNING;
> > > +
> > > + if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
> > > + val |= SDHCI_SUPPORT_HS400;
> > > }
> > > }
> > >
> > > @@ -774,6 +800,7 @@ static int esdhc_change_pinstate(struct sdhci_host
> > *host,
> > > break;
> > > case MMC_TIMING_UHS_SDR104:
> > > case MMC_TIMING_MMC_HS200:
> > > + case MMC_TIMING_MMC_HS400:
> > > pinctrl = imx_data->pins_200mhz;
> > > break;
> > > default:
> > > @@ -784,6 +811,44 @@ static int esdhc_change_pinstate(struct sdhci_host
> > *host,
> > > return pinctrl_select_state(imx_data->pinctrl, pinctrl); }
> > >
> > > +/*
> > > + * For HS400 eMMC, there is a data_strobe line, this signal is
> > > +generated
> > > + * by the device and used for data output and CRC status response
> > > +output
> > > + * in HS400 mode. The frequency of this signal follows the frequency
> > > +of
> > > + * CLK generated by host. Host receive the data which is aligned to
> > > +the
> > > + * edge of data_strobe line. Due to the time delay between CLK line
> > > +and
> > > + * data_strobe line, if the delay time is larger than one clock
> > > +cycle,
> > > + * then CLK and data_strobe line will misaligned, read error shows up.
> > > + * So when the CLK is higher than 100MHz, each clock cycle is short
> > > +enough,
> > > + * host should config the delay target.
> > > + */
> > > +static void esdhc_set_strobe_dll(struct sdhci_host *host) {
> > > + u32 v;
> > > +
> > > + if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
> > > + /* force a reset on strobe dll */
> > > + writel(ESDHC_STROBE_DLL_CTRL_RESET,
> > > + host->ioaddr + ESDHC_STROBE_DLL_CTRL);
> > > + /*
> > > + * enable strobe dll ctrl and adjust the delay target
> > > + * for the uSDHC loopback read clock
> > > + */
> > > + v = ESDHC_STROBE_DLL_CTRL_ENABLE |
> > > + (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
> > > + writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
> > > + /* wait 1us to make sure strobe dll status register stable */
> > > + udelay(1);
> > > + v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
> > > + if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
> > > + dev_warn(mmc_dev(host->mmc),
> > > + "warning! HS400 strobe DLL status REF not
> > lock!\n");
> > > + if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
> > > + dev_warn(mmc_dev(host->mmc),
> > > + "warning! HS400 strobe DLL status SLV not
> > lock!\n");
> > > + }
> > > +}
> > > +
> > > static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned
> > > timing) {
> > > struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -795,7
> > > +860,13 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host,
> > unsigned timing)
> > > case MMC_TIMING_UHS_SDR25:
> > > case MMC_TIMING_UHS_SDR50:
> > > case MMC_TIMING_UHS_SDR104:
> > > + break;
> > > case MMC_TIMING_MMC_HS200:
> > > + /* disable ddr mode and disable HS400 mode */
> > > + writel(readl(host->ioaddr + ESDHC_MIX_CTRL) &
> > > + ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN),
> > > + host->ioaddr + ESDHC_MIX_CTRL);
> > > + imx_data->is_ddr = 0;
> >
> > Can you please explain why need above code?
> > Any issues if not do it?
> >
> > Regards
> > Dong Aisheng
>
> [haibo] currently, mmc core code use mmc_retune before every request,
> and in mmc_retune, for eMMC, if the chip is in HS400 mode, firstly,
> the code use mmc_hs400_to_hs200() to change the emmc chip work in
> HS200 mode, and execute the tuning, and then use mmc_hs200_to_hs400()
> to make the emmc chip work back in HS400 mode.
>
> Without the above code, when change to hs200 mode from hs400 mode, our
> host still enable ddr mode and enable hs400 mode, which is wrong.
> So add the code here.
>

Okay, understand.
Clearing only for HS200 case is a bit confusing.
A common rule to do it might be:
Step 1) Clear exist timing setting at first
Step 2) Set the required timing for each case

I'd suggest you do like as follows:

m = readl(host->ioaddr + ESDHC_MIX_CTRL);
m &=~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
imx_data->is_ddr = 0;

Then
case HS200:
...
case HS400:
...

Regards
Dong Aisheng

> >
> > > break;
> > > case MMC_TIMING_UHS_DDR50:
> > > case MMC_TIMING_MMC_DDR52:
> > > @@ -813,6 +884,13 @@ static void esdhc_set_uhs_signaling(struct
> > sdhci_host *host, unsigned timing)
> > > writel(v, host->ioaddr + ESDHC_DLL_CTRL);
> > > }
> > > break;
> > > + case MMC_TIMING_MMC_HS400:
> > > + writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
> > > + ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN,
> > > + host->ioaddr + ESDHC_MIX_CTRL);
> > > + imx_data->is_ddr = 1;
> > > + esdhc_set_strobe_dll(host);
> > > + break;
> > > }
> > >
> > > esdhc_change_pinstate(host, timing); @@ -1100,6 +1178,9 @@ static
> > > int sdhci_esdhc_imx_probe(struct platform_device *pdev)
> > > if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
> > > host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
> > >
> > > + if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
> > > + host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
> > > +
> > > if (of_id)
> > > err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
> > > else
> > > --
> > > 1.9.1
> > >