* [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers
@ 2026-04-27 5:52 Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Kaustabh Chakraborty @ 2026-04-27 5:52 UTC (permalink / raw)
To: Peng Fan, u-boot
Cc: Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko,
Lukas Timmermann, Henrik Grimler, Kaustabh Chakraborty
Since upstream dts version v7.0+, Exynos 7870 devices have an explicit
`bus-width = <8>` property. This results in the U-Boot driver trying to
initialize the HS400 mode, which appears to fail. The HS400 support
added in the DW-MMC Exynos driver appears to have an incomplete init
sequence.
This patch series adds the missing pieces to enable HS400 support, and
additionally adds support for HS400ES too.
The last patch in the series also enables HS400ES support in the
exynos-mobile defconfig.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
Kaustabh Chakraborty (4):
mmc: exynos_dw_mmc: add proper init sequence for HS400 support
mmc: dw_mmc: setup set_enhanced_strobe ops in driver
mmc: exynos_dw_mmc: add support for HS400ES
configs: exynos-mobile: enable support for HS400ES in MMC driver
arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++
configs/exynos-mobile_defconfig | 1 +
drivers/mmc/dw_mmc.c | 19 +++++++
drivers/mmc/exynos_dw_mmc.c | 88 ++++++++++++++++++++++++++++++-
include/dwmmc.h | 10 ++++
5 files changed, 121 insertions(+), 2 deletions(-)
---
base-commit: 9f61fd5b80a43ae20ba115e3a2933d47d720ab82
change-id: 20260427-dwmmc-exynos-hs400-es-b77009a808ce
Best regards,
--
Kaustabh Chakraborty <kauschluss@disroot.org>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support
2026-04-27 5:52 [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty
@ 2026-04-27 5:52 ` Kaustabh Chakraborty
2026-04-30 17:26 ` Henrik Grimler
2026-04-27 5:52 ` [PATCH 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver Kaustabh Chakraborty
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Kaustabh Chakraborty @ 2026-04-27 5:52 UTC (permalink / raw)
To: Peng Fan, u-boot
Cc: Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko,
Lukas Timmermann, Henrik Grimler, Kaustabh Chakraborty
HS400 support was added, but configuration necessary for HS400 support
was left out. Add necessary changes, which includes:
- Device tree properties, such as "samsung,dw-mshc-hs400-timing" and
"samsung,read-strobe-delay", which function as per dt-bindings.
- Registers related to HS400, which are necessary to enable HS400+ support.
- Appropriate timing tunings for the HS400 mode.
Note that these changes are loosely based off of its Linux kernel
counterpart.
Fixes: bbe3b9fa0922 ("mmc: exynos_dw_mmc: add support for MMC HS200 and HS400 modes")
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++
drivers/mmc/exynos_dw_mmc.c | 80 +++++++++++++++++++++++++++++++
2 files changed, 85 insertions(+)
diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h
index 4432deedef7..50081326c25 100644
--- a/arch/arm/mach-exynos/include/mach/dwmmc.h
+++ b/arch/arm/mach-exynos/include/mach/dwmmc.h
@@ -15,6 +15,11 @@
#define DWMCI_SET_DRV_CLK(x) ((x) << 16)
#define DWMCI_SET_DIV_RATIO(x) ((x) << 24)
+/* HS400 Related Registers */
+#define DWMCI_HS400_DQS_EN 0x180
+#define DWMCI_HS400_ASYNC_FIFO_CTRL 0x184
+#define DWMCI_HS400_DLINE_CTRL 0x188
+
/* Protector Register */
#define DWMCI_EMMCP_BASE 0x1000
#define EMMCP_MPSECURITY (DWMCI_EMMCP_BASE + 0x0010)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 7ccd113bd79..d5e90a9bd5c 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -8,6 +8,7 @@
#include <dwmmc.h>
#include <asm/global_data.h>
#include <malloc.h>
+#include <mmc.h>
#include <errno.h>
#include <asm/arch/dwmmc.h>
#include <asm/arch/clk.h>
@@ -30,6 +31,14 @@
#define CLKSEL_UP_SAMPLE(x, y) (((x) & ~CLKSEL_CCLK_SAMPLE(7)) | \
CLKSEL_CCLK_SAMPLE(y))
+/* RCLK_EN register defines */
+#define DATA_STROBE_EN BIT(0)
+#define AXI_NON_BLOCKING_WR BIT(7)
+
+/* DLINE_CTRL register defines */
+#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF))
+#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF)
+
/**
* DOC: Quirk flags for different Exynos DW MMC blocks
*
@@ -71,6 +80,11 @@ struct dwmci_exynos_priv_data {
struct clk clk;
u32 sdr_timing;
u32 ddr_timing;
+ u32 hs400_timing;
+ u32 tuned_sample;
+ u32 dqs_delay;
+ u32 saved_dqs_en;
+ u32 saved_strobe_ctrl;
const struct exynos_dwmmc_variant *chip;
};
@@ -162,6 +176,27 @@ static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host)
& DWMCI_DIVRATIO_MASK) + 1;
}
+static void exynos_config_hs400(struct dwmci_host *host, enum bus_mode mode)
+{
+ struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+ u32 dqs, strobe;
+
+ dqs = priv->saved_dqs_en;
+ strobe = priv->saved_strobe_ctrl;
+
+ switch (mode) {
+ case MMC_HS_400:
+ dqs |= DATA_STROBE_EN;
+ strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
+ break;
+ default:
+ dqs &= ~DATA_STROBE_EN;
+ }
+
+ dwmci_writel(host, DWMCI_HS400_DQS_EN, dqs);
+ dwmci_writel(host, DWMCI_HS400_DLINE_CTRL, strobe);
+}
+
/* Configure CLKSEL register with chosen timing values */
static int exynos_dwmci_clksel(struct dwmci_host *host)
{
@@ -170,6 +205,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
u32 timing;
switch (host->mmc->selected_mode) {
+ case MMC_HS_400:
+ timing = CLKSEL_UP_SAMPLE(priv->hs400_timing, priv->tuned_sample);
+ break;
case MMC_DDR_52:
timing = priv->ddr_timing;
break;
@@ -186,6 +224,8 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
dwmci_writel(host, priv->chip->clksel, timing);
+ exynos_config_hs400(host, host->mmc->selected_mode);
+
return 0;
}
@@ -223,6 +263,16 @@ static void exynos_dwmci_board_init(struct dwmci_host *host)
{
struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+ if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) {
+ priv->saved_strobe_ctrl = dwmci_readl(host, DWMCI_HS400_DLINE_CTRL);
+ priv->saved_dqs_en = dwmci_readl(host, DWMCI_HS400_DQS_EN);
+ priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
+ dwmci_writel(host, DWMCI_HS400_DQS_EN, priv->saved_dqs_en);
+ if (!priv->dqs_delay)
+ priv->dqs_delay =
+ DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
+ }
+
if (priv->chip->quirks & DWMCI_QUIRK_DISABLE_SMU) {
dwmci_writel(host, EMMCP_MPSBEGIN0, 0);
dwmci_writel(host, EMMCP_SEND0, 0);
@@ -319,6 +369,22 @@ static int exynos_dwmmc_of_to_plat(struct udevice *dev)
DWMCI_SET_DIV_RATIO(div);
}
+ err = dev_read_u32_array(dev, "samsung,dw-mshc-hs400-timing", timing, 2);
+ if (err) {
+ debug("DWMMC%d: Can't get hs400-timings, using ddr-timings\n",
+ host->dev_index);
+ priv->hs400_timing = priv->ddr_timing;
+ } else {
+ priv->hs400_timing = DWMCI_SET_SAMPLE_CLK(timing[0]) |
+ DWMCI_SET_DRV_CLK(timing[1]) |
+ DWMCI_SET_DIV_RATIO(1);
+ if (dev_read_u32(dev, "samsung,read-strobe-delay", &priv->dqs_delay)) {
+ priv->dqs_delay = 0;
+ debug("DWMMC%d: read-strobe-delay is not found, assuming usage of default value\n",
+ host->dev_index);
+ }
+ }
+
host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0);
host->bus_hz = dev_read_u32_default(dev, "clock-frequency", 0);
@@ -356,6 +422,16 @@ static int exynos_dwmmc_get_best_clksmpl(u8 candidates)
return -EIO;
}
+static int dw_mci_exynos_prepare_hs400_tuning(struct dwmci_host *host)
+{
+ struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+
+ dwmci_writel(host, priv->chip->clksel, priv->hs400_timing);
+ host->bus_hz = exynos_dwmci_get_clk(host, host->clock);
+
+ return 0;
+}
+
static int exynos_dwmmc_execute_tuning(struct udevice *dev, u32 opcode)
{
struct dwmci_exynos_priv_data *priv = dev_get_priv(dev);
@@ -365,6 +441,9 @@ static int exynos_dwmmc_execute_tuning(struct udevice *dev, u32 opcode)
u32 clksel;
int ret;
+ if (mmc->hs400_tuning)
+ dw_mci_exynos_prepare_hs400_tuning(host);
+
clksel = dwmci_readl(host, priv->chip->clksel);
start_smpl = CLKSEL_CCLK_SAMPLE(clksel);
@@ -387,6 +466,7 @@ static int exynos_dwmmc_execute_tuning(struct udevice *dev, u32 opcode)
return ret;
}
+ priv->tuned_sample = ret;
dwmci_writel(host, priv->chip->clksel, CLKSEL_UP_SAMPLE(clksel, ret));
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver
2026-04-27 5:52 [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty
@ 2026-04-27 5:52 ` Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 3/4] mmc: exynos_dw_mmc: add support for HS400ES Kaustabh Chakraborty
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Kaustabh Chakraborty @ 2026-04-27 5:52 UTC (permalink / raw)
To: Peng Fan, u-boot
Cc: Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko,
Lukas Timmermann, Henrik Grimler, Kaustabh Chakraborty
At the end of HS400ES initialization by the core mmc driver,
mmc_set_enhanced_strobe() is called. This expects the host driver to
have an ops function for it.
The DW-MMC driver does not have it, thus the call fails with -ENOSUPP.
Add the ops function to allow control to be passed to the host side.
Since DW-MMC driver implementation depends on the platform, the above
ops function is made to pass its control to the implementation-specific
function available in the platform-specific driver.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
drivers/mmc/dw_mmc.c | 19 +++++++++++++++++++
include/dwmmc.h | 10 ++++++++++
2 files changed, 29 insertions(+)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index d9c05b223d5..0a245d72881 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -730,6 +730,19 @@ static int dwmci_init(struct mmc *mmc)
return 0;
}
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+static int dwmci_set_enhanced_strobe(struct udevice *dev)
+{
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+ struct dwmci_host *host = mmc->priv;
+
+ if (host && host->set_enhanced_strobe)
+ return host->set_enhanced_strobe(host);
+
+ return 0;
+}
+#endif
+
#if CONFIG_IS_ENABLED(DM_MMC)
int dwmci_probe(struct udevice *dev)
{
@@ -741,6 +754,9 @@ int dwmci_probe(struct udevice *dev)
const struct dm_mmc_ops dm_dwmci_ops = {
.send_cmd = dwmci_send_cmd,
.set_ios = dwmci_set_ios,
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+ .set_enhanced_strobe = dwmci_set_enhanced_strobe,
+#endif
};
#else
@@ -748,6 +764,9 @@ static const struct mmc_ops dwmci_ops = {
.send_cmd = dwmci_send_cmd,
.set_ios = dwmci_set_ios,
.init = dwmci_init,
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+ .set_enhanced_strobe = dwmci_set_enhanced_strobe,
+#endif
};
#endif
diff --git a/include/dwmmc.h b/include/dwmmc.h
index 7e1a6646518..49488709a82 100644
--- a/include/dwmmc.h
+++ b/include/dwmmc.h
@@ -225,6 +225,16 @@ struct dwmci_host {
* return that value too. Then DWMMC will put itself in bypass mode.
*/
unsigned int (*get_mmc_clk)(struct dwmci_host *host, uint freq);
+ /**
+ * @set_enhanced_strobe: (Optional) Platform function to run on enabling
+ * HS400ES strobe
+ *
+ * @host: DWMMC host
+ *
+ * This is used to enable the enhanced strobe for the HS400ES MMC mode.
+ * The caller invokes this at the end of the HS400ES frequency dance.
+ */
+ unsigned int (*set_enhanced_strobe)(struct dwmci_host *host);
#ifndef CONFIG_BLK
struct mmc_config cfg;
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/4] mmc: exynos_dw_mmc: add support for HS400ES
2026-04-27 5:52 [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver Kaustabh Chakraborty
@ 2026-04-27 5:52 ` Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver Kaustabh Chakraborty
2026-04-27 20:23 ` [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Henrik Grimler
4 siblings, 0 replies; 10+ messages in thread
From: Kaustabh Chakraborty @ 2026-04-27 5:52 UTC (permalink / raw)
To: Peng Fan, u-boot
Cc: Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko,
Lukas Timmermann, Henrik Grimler, Kaustabh Chakraborty
Add support for HS400ES. This is trivial as no new init sequence is
required in the driver, add it to the host driver capabilities, and
direct the HS400ES mode on reusing the HS400 init sequence.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
drivers/mmc/exynos_dw_mmc.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index d5e90a9bd5c..34483e4c5a3 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -185,6 +185,7 @@ static void exynos_config_hs400(struct dwmci_host *host, enum bus_mode mode)
strobe = priv->saved_strobe_ctrl;
switch (mode) {
+ case MMC_HS_400_ES:
case MMC_HS_400:
dqs |= DATA_STROBE_EN;
strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
@@ -205,6 +206,7 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
u32 timing;
switch (host->mmc->selected_mode) {
+ case MMC_HS_400_ES:
case MMC_HS_400:
timing = CLKSEL_UP_SAMPLE(priv->hs400_timing, priv->tuned_sample);
break;
@@ -238,7 +240,8 @@ static unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
/* Should be double rate for DDR or HS mode */
if ((host->mmc->selected_mode == MMC_DDR_52 &&
host->mmc->bus_width == 8) ||
- host->mmc->selected_mode == MMC_HS_400) {
+ host->mmc->selected_mode == MMC_HS_400 ||
+ host->mmc->selected_mode == MMC_HS_400_ES) {
freq *= 2;
}
@@ -263,7 +266,8 @@ static void exynos_dwmci_board_init(struct dwmci_host *host)
{
struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
- if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) {
+ if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) ||
+ CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)) {
priv->saved_strobe_ctrl = dwmci_readl(host, DWMCI_HS400_DLINE_CTRL);
priv->saved_dqs_en = dwmci_readl(host, DWMCI_HS400_DQS_EN);
priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
@@ -521,7 +525,7 @@ static int exynos_dwmmc_probe(struct udevice *dev)
host->name = dev->name;
host->board_init = exynos_dwmci_board_init;
host->caps = MMC_MODE_DDR_52MHz | MMC_MODE_HS200 | MMC_MODE_HS400 |
- UHS_CAPS;
+ MMC_MODE_HS400_ES | UHS_CAPS;
host->clksel = exynos_dwmci_clksel;
host->get_mmc_clk = exynos_dwmci_get_clk;
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver
2026-04-27 5:52 [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty
` (2 preceding siblings ...)
2026-04-27 5:52 ` [PATCH 3/4] mmc: exynos_dw_mmc: add support for HS400ES Kaustabh Chakraborty
@ 2026-04-27 5:52 ` Kaustabh Chakraborty
2026-04-27 20:23 ` [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Henrik Grimler
4 siblings, 0 replies; 10+ messages in thread
From: Kaustabh Chakraborty @ 2026-04-27 5:52 UTC (permalink / raw)
To: Peng Fan, u-boot
Cc: Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko,
Lukas Timmermann, Henrik Grimler, Kaustabh Chakraborty
MMC0 in Exynos7870 devices (supported by this board variant) can be
initialized in HS400ES mode. Enable the respective config option.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
configs/exynos-mobile_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/configs/exynos-mobile_defconfig b/configs/exynos-mobile_defconfig
index bff4e6c6fb7..6085c294973 100644
--- a/configs/exynos-mobile_defconfig
+++ b/configs/exynos-mobile_defconfig
@@ -48,6 +48,7 @@ CONFIG_MISC=y
CONFIG_MMC_BROKEN_CD=y
CONFIG_MMC_IO_VOLTAGE=y
CONFIG_MMC_UHS_SUPPORT=y
+CONFIG_MMC_HS400_ES_SUPPORT=y
CONFIG_MMC_HS400_SUPPORT=y
CONFIG_MMC_DW=y
CONFIG_PHY=y
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers
2026-04-27 5:52 [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty
` (3 preceding siblings ...)
2026-04-27 5:52 ` [PATCH 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver Kaustabh Chakraborty
@ 2026-04-27 20:23 ` Henrik Grimler
4 siblings, 0 replies; 10+ messages in thread
From: Henrik Grimler @ 2026-04-27 20:23 UTC (permalink / raw)
To: Kaustabh Chakraborty, Peng Fan, u-boot
Cc: Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko,
Lukas Timmermann
Hi Kaustabh,
On Mon, 2026-04-27 at 11:22 +0530, Kaustabh Chakraborty wrote:
> Since upstream dts version v7.0+, Exynos 7870 devices have an
> explicit
> `bus-width = <8>` property. This results in the U-Boot driver trying
> to
> initialize the HS400 mode, which appears to fail. The HS400 support
> added in the DW-MMC Exynos driver appears to have an incomplete init
> sequence.
>
> This patch series adds the missing pieces to enable HS400 support,
> and
> additionally adds support for HS400ES too.
Seems like exynos5422-odroid-xu4 can support hs400 as well based on
Hardkernel's device trees [1]. I will try to test on that device during
the week to get some more test coverage for this series.
[1]
https://github.com/hardkernel/linux/blob/odroidxu4-6.1.y/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi#L491
Best regards,
Henrik Grimler
> The last patch in the series also enables HS400ES support in the
> exynos-mobile defconfig.
>
> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> ---
> Kaustabh Chakraborty (4):
> mmc: exynos_dw_mmc: add proper init sequence for HS400 support
> mmc: dw_mmc: setup set_enhanced_strobe ops in driver
> mmc: exynos_dw_mmc: add support for HS400ES
> configs: exynos-mobile: enable support for HS400ES in MMC
> driver
>
> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++
> configs/exynos-mobile_defconfig | 1 +
> drivers/mmc/dw_mmc.c | 19 +++++++
> drivers/mmc/exynos_dw_mmc.c | 88
> ++++++++++++++++++++++++++++++-
> include/dwmmc.h | 10 ++++
> 5 files changed, 121 insertions(+), 2 deletions(-)
> ---
> base-commit: 9f61fd5b80a43ae20ba115e3a2933d47d720ab82
> change-id: 20260427-dwmmc-exynos-hs400-es-b77009a808ce
>
> Best regards,
> --
> Kaustabh Chakraborty <kauschluss@disroot.org>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support
2026-04-27 5:52 ` [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty
@ 2026-04-30 17:26 ` Henrik Grimler
2026-04-30 19:41 ` Kaustabh Chakraborty
0 siblings, 1 reply; 10+ messages in thread
From: Henrik Grimler @ 2026-04-30 17:26 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung,
Anand Moon, Sam Protsenko, Lukas Timmermann
Hi Kaustabh,
On Mon, Apr 27, 2026 at 11:22:49AM +0530, Kaustabh Chakraborty wrote:
> HS400 support was added, but configuration necessary for HS400 support
> was left out. Add necessary changes, which includes:
> - Device tree properties, such as "samsung,dw-mshc-hs400-timing" and
> "samsung,read-strobe-delay", which function as per dt-bindings.
> - Registers related to HS400, which are necessary to enable HS400+ support.
> - Appropriate timing tunings for the HS400 mode.
>
> Note that these changes are loosely based off of its Linux kernel
> counterpart.
>
> Fixes: bbe3b9fa0922 ("mmc: exynos_dw_mmc: add support for MMC HS200 and HS400 modes")
> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> ---
> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++
> drivers/mmc/exynos_dw_mmc.c | 80 +++++++++++++++++++++++++++++++
> 2 files changed, 85 insertions(+)
>
> diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h
> index 4432deedef7..50081326c25 100644
> --- a/arch/arm/mach-exynos/include/mach/dwmmc.h
> +++ b/arch/arm/mach-exynos/include/mach/dwmmc.h
> @@ -15,6 +15,11 @@
> #define DWMCI_SET_DRV_CLK(x) ((x) << 16)
> #define DWMCI_SET_DIV_RATIO(x) ((x) << 24)
>
> +/* HS400 Related Registers */
> +#define DWMCI_HS400_DQS_EN 0x180
> +#define DWMCI_HS400_ASYNC_FIFO_CTRL 0x184
> +#define DWMCI_HS400_DLINE_CTRL 0x188
> +
> /* Protector Register */
> #define DWMCI_EMMCP_BASE 0x1000
> #define EMMCP_MPSECURITY (DWMCI_EMMCP_BASE + 0x0010)
> diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
> index 7ccd113bd79..d5e90a9bd5c 100644
> --- a/drivers/mmc/exynos_dw_mmc.c
> +++ b/drivers/mmc/exynos_dw_mmc.c
> @@ -8,6 +8,7 @@
> #include <dwmmc.h>
> #include <asm/global_data.h>
> #include <malloc.h>
> +#include <mmc.h>
> #include <errno.h>
> #include <asm/arch/dwmmc.h>
> #include <asm/arch/clk.h>
> @@ -30,6 +31,14 @@
> #define CLKSEL_UP_SAMPLE(x, y) (((x) & ~CLKSEL_CCLK_SAMPLE(7)) | \
> CLKSEL_CCLK_SAMPLE(y))
>
> +/* RCLK_EN register defines */
> +#define DATA_STROBE_EN BIT(0)
> +#define AXI_NON_BLOCKING_WR BIT(7)
> +
> +/* DLINE_CTRL register defines */
> +#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF))
> +#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF)
> +
> /**
> * DOC: Quirk flags for different Exynos DW MMC blocks
> *
> @@ -71,6 +80,11 @@ struct dwmci_exynos_priv_data {
> struct clk clk;
> u32 sdr_timing;
> u32 ddr_timing;
> + u32 hs400_timing;
> + u32 tuned_sample;
> + u32 dqs_delay;
> + u32 saved_dqs_en;
> + u32 saved_strobe_ctrl;
> const struct exynos_dwmmc_variant *chip;
> };
>
> @@ -162,6 +176,27 @@ static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host)
> & DWMCI_DIVRATIO_MASK) + 1;
> }
>
> +static void exynos_config_hs400(struct dwmci_host *host, enum bus_mode mode)
> +{
> + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
> + u32 dqs, strobe;
> +
> + dqs = priv->saved_dqs_en;
> + strobe = priv->saved_strobe_ctrl;
> +
> + switch (mode) {
> + case MMC_HS_400:
> + dqs |= DATA_STROBE_EN;
> + strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
> + break;
> + default:
> + dqs &= ~DATA_STROBE_EN;
> + }
> +
> + dwmci_writel(host, DWMCI_HS400_DQS_EN, dqs);
> + dwmci_writel(host, DWMCI_HS400_DLINE_CTRL, strobe);
> +}
> +
> /* Configure CLKSEL register with chosen timing values */
> static int exynos_dwmci_clksel(struct dwmci_host *host)
> {
> @@ -170,6 +205,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
> u32 timing;
>
> switch (host->mmc->selected_mode) {
> + case MMC_HS_400:
> + timing = CLKSEL_UP_SAMPLE(priv->hs400_timing, priv->tuned_sample);
> + break;
> case MMC_DDR_52:
> timing = priv->ddr_timing;
> break;
> @@ -186,6 +224,8 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
>
> dwmci_writel(host, priv->chip->clksel, timing);
>
> + exynos_config_hs400(host, host->mmc->selected_mode);
> +
This breaks emmc in Linux for existing devices without hs400 support:
exynos_config_hs400 is run no matter if the dt has the hs400
parameters, meaning 0x0 is written to DWMCI_HS400_DQS_EN and
DWMCI_HS400_DLINE_CTRL. U-boot does not seem to care, it loads the
kernel and initramfs without issues, but Linux later gives errors:
```
[ 20.741025] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 400000Hz, actual 396825HZ div = 63)
[ 20.886686] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
[ 20.895138] mmc_host mmc0: Bus speed (slot 0) = 200000000Hz (slot req 200000000Hz, actual 200000000HZ div = 0)
[ 20.905990] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
[ 20.914988] mmc_host mmc0: Bus speed (slot 0) = 400000000Hz (slot req 200000000Hz, actual 200000000HZ div = 1)
[ 21.099546] I/O error, dev mmcblk0, sector 1000001 op 0x1:(WRITE) flags 0x800 phys_seg 16 prio class 2
[ 21.099853] I/O error, dev mmcblk0, sector 1000825 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
[ 21.107443] Buffer I/O error on dev mmcblk0p2, logical block 0, lost async page write
[ 21.107473] Buffer I/O error on dev mmcblk0p2, logical block 1, lost async page write
[ 21.116657] Buffer I/O error on dev mmcblk0p2, logical block 824, lost async page write
[ 21.124408] Buffer I/O error on dev mmcblk0p2, logical block 2, lost async page write
[ 21.132206] Buffer I/O error on dev mmcblk0p2, logical block 825, lost async page write
[ 21.140178] Buffer I/O error on dev mmcblk0p2, logical block 3, lost async page write
[ 21.147973] Buffer I/O error on dev mmcblk0p2, logical block 826, lost async page write
[ 21.155960] Buffer I/O error on dev mmcblk0p2, logical block 4, lost async page write
[ 21.163749] Buffer I/O error on dev mmcblk0p2, logical block 827, lost async page write
[ 21.171720] Buffer I/O error on dev mmcblk0p2, logical block 5, lost async page write
[ 21.200708] I/O error, dev mmcblk0, sector 1000873 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
[ 21.201043] I/O error, dev mmcblk0, sector 1000929 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
[ 21.218287] I/O error, dev mmcblk0, sector 1000945 op 0x1:(WRITE) flags 0x800 phys_seg 80 prio class 2
[ 21.218623] I/O error, dev mmcblk0, sector 1001033 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
[ 21.236813] I/O error, dev mmcblk0, sector 1001097 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
[ 21.237146] I/O error, dev mmcblk0, sector 1001121 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
[ 21.255370] I/O error, dev mmcblk0, sector 1001161 op 0x1:(WRITE) flags 0x800 phys_seg 56 prio class 2
[ 21.256253] I/O error, dev mmcblk0, sector 1001249 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
```
Specifically it seems to be writing 0x0 to DWMCI_HS400_DQS_EN that is
the issue, if I comment out that line it works also for my odroid-xu4
in non-hs400 mode.
Tested on odroid-xu4 with odroid-xu3_defconfig after applying the
entire series, without any other changes.
Best regards,
Henrik Grimler
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support
2026-04-30 17:26 ` Henrik Grimler
@ 2026-04-30 19:41 ` Kaustabh Chakraborty
2026-05-01 6:58 ` Henrik Grimler
0 siblings, 1 reply; 10+ messages in thread
From: Kaustabh Chakraborty @ 2026-04-30 19:41 UTC (permalink / raw)
To: Henrik Grimler, Kaustabh Chakraborty
Cc: Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung,
Anand Moon, Sam Protsenko, Lukas Timmermann
On 2026-04-30 19:26 +02:00, Henrik Grimler wrote:
> Hi Kaustabh,
>
> On Mon, Apr 27, 2026 at 11:22:49AM +0530, Kaustabh Chakraborty wrote:
>> HS400 support was added, but configuration necessary for HS400 support
>> was left out. Add necessary changes, which includes:
>> - Device tree properties, such as "samsung,dw-mshc-hs400-timing" and
>> "samsung,read-strobe-delay", which function as per dt-bindings.
>> - Registers related to HS400, which are necessary to enable HS400+ support.
>> - Appropriate timing tunings for the HS400 mode.
>>
>> Note that these changes are loosely based off of its Linux kernel
>> counterpart.
>>
>> Fixes: bbe3b9fa0922 ("mmc: exynos_dw_mmc: add support for MMC HS200 and HS400 modes")
>> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
>> ---
>> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++
>> drivers/mmc/exynos_dw_mmc.c | 80 +++++++++++++++++++++++++++++++
>> 2 files changed, 85 insertions(+)
>>
>> diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h
>> index 4432deedef7..50081326c25 100644
>> --- a/arch/arm/mach-exynos/include/mach/dwmmc.h
>> +++ b/arch/arm/mach-exynos/include/mach/dwmmc.h
>> @@ -15,6 +15,11 @@
>> #define DWMCI_SET_DRV_CLK(x) ((x) << 16)
>> #define DWMCI_SET_DIV_RATIO(x) ((x) << 24)
>>
>> +/* HS400 Related Registers */
>> +#define DWMCI_HS400_DQS_EN 0x180
>> +#define DWMCI_HS400_ASYNC_FIFO_CTRL 0x184
>> +#define DWMCI_HS400_DLINE_CTRL 0x188
>> +
>> /* Protector Register */
>> #define DWMCI_EMMCP_BASE 0x1000
>> #define EMMCP_MPSECURITY (DWMCI_EMMCP_BASE + 0x0010)
>> diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
>> index 7ccd113bd79..d5e90a9bd5c 100644
>> --- a/drivers/mmc/exynos_dw_mmc.c
>> +++ b/drivers/mmc/exynos_dw_mmc.c
>> @@ -8,6 +8,7 @@
>> #include <dwmmc.h>
>> #include <asm/global_data.h>
>> #include <malloc.h>
>> +#include <mmc.h>
>> #include <errno.h>
>> #include <asm/arch/dwmmc.h>
>> #include <asm/arch/clk.h>
>> @@ -30,6 +31,14 @@
>> #define CLKSEL_UP_SAMPLE(x, y) (((x) & ~CLKSEL_CCLK_SAMPLE(7)) | \
>> CLKSEL_CCLK_SAMPLE(y))
>>
>> +/* RCLK_EN register defines */
>> +#define DATA_STROBE_EN BIT(0)
>> +#define AXI_NON_BLOCKING_WR BIT(7)
>> +
>> +/* DLINE_CTRL register defines */
>> +#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF))
>> +#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF)
>> +
>> /**
>> * DOC: Quirk flags for different Exynos DW MMC blocks
>> *
>> @@ -71,6 +80,11 @@ struct dwmci_exynos_priv_data {
>> struct clk clk;
>> u32 sdr_timing;
>> u32 ddr_timing;
>> + u32 hs400_timing;
>> + u32 tuned_sample;
>> + u32 dqs_delay;
>> + u32 saved_dqs_en;
>> + u32 saved_strobe_ctrl;
>> const struct exynos_dwmmc_variant *chip;
>> };
>>
>> @@ -162,6 +176,27 @@ static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host)
>> & DWMCI_DIVRATIO_MASK) + 1;
>> }
>>
>> +static void exynos_config_hs400(struct dwmci_host *host, enum bus_mode mode)
>> +{
>> + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
>> + u32 dqs, strobe;
>> +
>> + dqs = priv->saved_dqs_en;
>> + strobe = priv->saved_strobe_ctrl;
>> +
>> + switch (mode) {
>> + case MMC_HS_400:
>> + dqs |= DATA_STROBE_EN;
>> + strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
>> + break;
>> + default:
>> + dqs &= ~DATA_STROBE_EN;
>> + }
>> +
>> + dwmci_writel(host, DWMCI_HS400_DQS_EN, dqs);
>> + dwmci_writel(host, DWMCI_HS400_DLINE_CTRL, strobe);
>> +}
>> +
>> /* Configure CLKSEL register with chosen timing values */
>> static int exynos_dwmci_clksel(struct dwmci_host *host)
>> {
>> @@ -170,6 +205,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
>> u32 timing;
>>
>> switch (host->mmc->selected_mode) {
>> + case MMC_HS_400:
>> + timing = CLKSEL_UP_SAMPLE(priv->hs400_timing, priv->tuned_sample);
>> + break;
>> case MMC_DDR_52:
>> timing = priv->ddr_timing;
>> break;
>> @@ -186,6 +224,8 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
>>
>> dwmci_writel(host, priv->chip->clksel, timing);
>>
>> + exynos_config_hs400(host, host->mmc->selected_mode);
>> +
>
> This breaks emmc in Linux for existing devices without hs400 support:
> exynos_config_hs400 is run no matter if the dt has the hs400
> parameters, meaning 0x0 is written to DWMCI_HS400_DQS_EN and
> DWMCI_HS400_DLINE_CTRL. U-boot does not seem to care, it loads the
> kernel and initramfs without issues, but Linux later gives errors:
Is it that the same defconfig is used for two devices, one with and one
without HS400 support? If that's the case, guarding the call with
HS400_SUPPORT config options won't be a real fix.
mmc_of_parse() in mmc-uclass.h seems promising, this needs to be
integrated with dwmci_setup_cfg() in dw_mmc.c, and this change _may_ be
regressive in nature, so I may also need to add some safety guards for
this.
I will thus add this patch in the next rev. Would you be up to test the patch
for the concerned devices too?
>
> ```
> [ 20.741025] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 400000Hz, actual 396825HZ div = 63)
> [ 20.886686] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
> [ 20.895138] mmc_host mmc0: Bus speed (slot 0) = 200000000Hz (slot req 200000000Hz, actual 200000000HZ div = 0)
> [ 20.905990] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
> [ 20.914988] mmc_host mmc0: Bus speed (slot 0) = 400000000Hz (slot req 200000000Hz, actual 200000000HZ div = 1)
> [ 21.099546] I/O error, dev mmcblk0, sector 1000001 op 0x1:(WRITE) flags 0x800 phys_seg 16 prio class 2
> [ 21.099853] I/O error, dev mmcblk0, sector 1000825 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> [ 21.107443] Buffer I/O error on dev mmcblk0p2, logical block 0, lost async page write
> [ 21.107473] Buffer I/O error on dev mmcblk0p2, logical block 1, lost async page write
> [ 21.116657] Buffer I/O error on dev mmcblk0p2, logical block 824, lost async page write
> [ 21.124408] Buffer I/O error on dev mmcblk0p2, logical block 2, lost async page write
> [ 21.132206] Buffer I/O error on dev mmcblk0p2, logical block 825, lost async page write
> [ 21.140178] Buffer I/O error on dev mmcblk0p2, logical block 3, lost async page write
> [ 21.147973] Buffer I/O error on dev mmcblk0p2, logical block 826, lost async page write
> [ 21.155960] Buffer I/O error on dev mmcblk0p2, logical block 4, lost async page write
> [ 21.163749] Buffer I/O error on dev mmcblk0p2, logical block 827, lost async page write
> [ 21.171720] Buffer I/O error on dev mmcblk0p2, logical block 5, lost async page write
> [ 21.200708] I/O error, dev mmcblk0, sector 1000873 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> [ 21.201043] I/O error, dev mmcblk0, sector 1000929 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> [ 21.218287] I/O error, dev mmcblk0, sector 1000945 op 0x1:(WRITE) flags 0x800 phys_seg 80 prio class 2
> [ 21.218623] I/O error, dev mmcblk0, sector 1001033 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
> [ 21.236813] I/O error, dev mmcblk0, sector 1001097 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> [ 21.237146] I/O error, dev mmcblk0, sector 1001121 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
> [ 21.255370] I/O error, dev mmcblk0, sector 1001161 op 0x1:(WRITE) flags 0x800 phys_seg 56 prio class 2
> [ 21.256253] I/O error, dev mmcblk0, sector 1001249 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> ```
>
> Specifically it seems to be writing 0x0 to DWMCI_HS400_DQS_EN that is
> the issue, if I comment out that line it works also for my odroid-xu4
> in non-hs400 mode.
>
> Tested on odroid-xu4 with odroid-xu3_defconfig after applying the
> entire series, without any other changes.
>
> Best regards,
> Henrik Grimler
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support
2026-04-30 19:41 ` Kaustabh Chakraborty
@ 2026-05-01 6:58 ` Henrik Grimler
2026-05-01 9:02 ` Kaustabh Chakraborty
0 siblings, 1 reply; 10+ messages in thread
From: Henrik Grimler @ 2026-05-01 6:58 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung,
Anand Moon, Sam Protsenko, Lukas Timmermann
Hi Kaustabh,
On Fri, May 01, 2026 at 01:11:29AM +0530, Kaustabh Chakraborty wrote:
> On 2026-04-30 19:26 +02:00, Henrik Grimler wrote:
> > Hi Kaustabh,
> >
> > On Mon, Apr 27, 2026 at 11:22:49AM +0530, Kaustabh Chakraborty wrote:
> >> HS400 support was added, but configuration necessary for HS400 support
> >> was left out. Add necessary changes, which includes:
> >> - Device tree properties, such as "samsung,dw-mshc-hs400-timing" and
> >> "samsung,read-strobe-delay", which function as per dt-bindings.
> >> - Registers related to HS400, which are necessary to enable HS400+ support.
> >> - Appropriate timing tunings for the HS400 mode.
> >>
> >> Note that these changes are loosely based off of its Linux kernel
> >> counterpart.
> >>
> >> Fixes: bbe3b9fa0922 ("mmc: exynos_dw_mmc: add support for MMC HS200 and HS400 modes")
> >> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> >> ---
> >> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++
> >> drivers/mmc/exynos_dw_mmc.c | 80 +++++++++++++++++++++++++++++++
> >> 2 files changed, 85 insertions(+)
> >>
> >> diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h
> >> index 4432deedef7..50081326c25 100644
> >> --- a/arch/arm/mach-exynos/include/mach/dwmmc.h
> >> +++ b/arch/arm/mach-exynos/include/mach/dwmmc.h
> >> @@ -15,6 +15,11 @@
> >> #define DWMCI_SET_DRV_CLK(x) ((x) << 16)
> >> #define DWMCI_SET_DIV_RATIO(x) ((x) << 24)
> >>
> >> +/* HS400 Related Registers */
> >> +#define DWMCI_HS400_DQS_EN 0x180
> >> +#define DWMCI_HS400_ASYNC_FIFO_CTRL 0x184
> >> +#define DWMCI_HS400_DLINE_CTRL 0x188
> >> +
> >> /* Protector Register */
> >> #define DWMCI_EMMCP_BASE 0x1000
> >> #define EMMCP_MPSECURITY (DWMCI_EMMCP_BASE + 0x0010)
> >> diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
> >> index 7ccd113bd79..d5e90a9bd5c 100644
> >> --- a/drivers/mmc/exynos_dw_mmc.c
> >> +++ b/drivers/mmc/exynos_dw_mmc.c
> >> @@ -8,6 +8,7 @@
> >> #include <dwmmc.h>
> >> #include <asm/global_data.h>
> >> #include <malloc.h>
> >> +#include <mmc.h>
> >> #include <errno.h>
> >> #include <asm/arch/dwmmc.h>
> >> #include <asm/arch/clk.h>
> >> @@ -30,6 +31,14 @@
> >> #define CLKSEL_UP_SAMPLE(x, y) (((x) & ~CLKSEL_CCLK_SAMPLE(7)) | \
> >> CLKSEL_CCLK_SAMPLE(y))
> >>
> >> +/* RCLK_EN register defines */
> >> +#define DATA_STROBE_EN BIT(0)
> >> +#define AXI_NON_BLOCKING_WR BIT(7)
> >> +
> >> +/* DLINE_CTRL register defines */
> >> +#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF))
> >> +#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF)
> >> +
> >> /**
> >> * DOC: Quirk flags for different Exynos DW MMC blocks
> >> *
> >> @@ -71,6 +80,11 @@ struct dwmci_exynos_priv_data {
> >> struct clk clk;
> >> u32 sdr_timing;
> >> u32 ddr_timing;
> >> + u32 hs400_timing;
> >> + u32 tuned_sample;
> >> + u32 dqs_delay;
> >> + u32 saved_dqs_en;
> >> + u32 saved_strobe_ctrl;
> >> const struct exynos_dwmmc_variant *chip;
> >> };
> >>
> >> @@ -162,6 +176,27 @@ static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host)
> >> & DWMCI_DIVRATIO_MASK) + 1;
> >> }
> >>
> >> +static void exynos_config_hs400(struct dwmci_host *host, enum bus_mode mode)
> >> +{
> >> + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
> >> + u32 dqs, strobe;
> >> +
> >> + dqs = priv->saved_dqs_en;
> >> + strobe = priv->saved_strobe_ctrl;
> >> +
> >> + switch (mode) {
> >> + case MMC_HS_400:
> >> + dqs |= DATA_STROBE_EN;
> >> + strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
> >> + break;
> >> + default:
> >> + dqs &= ~DATA_STROBE_EN;
> >> + }
> >> +
> >> + dwmci_writel(host, DWMCI_HS400_DQS_EN, dqs);
> >> + dwmci_writel(host, DWMCI_HS400_DLINE_CTRL, strobe);
> >> +}
> >> +
> >> /* Configure CLKSEL register with chosen timing values */
> >> static int exynos_dwmci_clksel(struct dwmci_host *host)
> >> {
> >> @@ -170,6 +205,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
> >> u32 timing;
> >>
> >> switch (host->mmc->selected_mode) {
> >> + case MMC_HS_400:
> >> + timing = CLKSEL_UP_SAMPLE(priv->hs400_timing, priv->tuned_sample);
> >> + break;
> >> case MMC_DDR_52:
> >> timing = priv->ddr_timing;
> >> break;
> >> @@ -186,6 +224,8 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
> >>
> >> dwmci_writel(host, priv->chip->clksel, timing);
> >>
> >> + exynos_config_hs400(host, host->mmc->selected_mode);
> >> +
> >
> > This breaks emmc in Linux for existing devices without hs400 support:
> > exynos_config_hs400 is run no matter if the dt has the hs400
> > parameters, meaning 0x0 is written to DWMCI_HS400_DQS_EN and
> > DWMCI_HS400_DLINE_CTRL. U-boot does not seem to care, it loads the
> > kernel and initramfs without issues, but Linux later gives errors:
>
> Is it that the same defconfig is used for two devices, one with and one
> without HS400 support? If that's the case, guarding the call with
> HS400_SUPPORT config options won't be a real fix.
A family of devices are using the same defconfig. I assume the
hardware in all of them supports hs400, but at the moment only one
device seems to be configured to use hs400 _in Linux_, and none in
u-boot.
Adding a config guard around exynos_config_hs400() would probably
work. I see Linux uses a check like
if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420)
instead.
> mmc_of_parse() in mmc-uclass.h seems promising, this needs to be
> integrated with dwmci_setup_cfg() in dw_mmc.c, and this change _may_ be
> regressive in nature, so I may also need to add some safety guards for
> this.
>
> I will thus add this patch in the next rev. Would you be up to test the patch
> for the concerned devices too?
Yeah I can test on these older devices.
Best regards,
Henrik Grimler
> >
> > ```
> > [ 20.741025] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 400000Hz, actual 396825HZ div = 63)
> > [ 20.886686] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
> > [ 20.895138] mmc_host mmc0: Bus speed (slot 0) = 200000000Hz (slot req 200000000Hz, actual 200000000HZ div = 0)
> > [ 20.905990] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
> > [ 20.914988] mmc_host mmc0: Bus speed (slot 0) = 400000000Hz (slot req 200000000Hz, actual 200000000HZ div = 1)
> > [ 21.099546] I/O error, dev mmcblk0, sector 1000001 op 0x1:(WRITE) flags 0x800 phys_seg 16 prio class 2
> > [ 21.099853] I/O error, dev mmcblk0, sector 1000825 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> > [ 21.107443] Buffer I/O error on dev mmcblk0p2, logical block 0, lost async page write
> > [ 21.107473] Buffer I/O error on dev mmcblk0p2, logical block 1, lost async page write
> > [ 21.116657] Buffer I/O error on dev mmcblk0p2, logical block 824, lost async page write
> > [ 21.124408] Buffer I/O error on dev mmcblk0p2, logical block 2, lost async page write
> > [ 21.132206] Buffer I/O error on dev mmcblk0p2, logical block 825, lost async page write
> > [ 21.140178] Buffer I/O error on dev mmcblk0p2, logical block 3, lost async page write
> > [ 21.147973] Buffer I/O error on dev mmcblk0p2, logical block 826, lost async page write
> > [ 21.155960] Buffer I/O error on dev mmcblk0p2, logical block 4, lost async page write
> > [ 21.163749] Buffer I/O error on dev mmcblk0p2, logical block 827, lost async page write
> > [ 21.171720] Buffer I/O error on dev mmcblk0p2, logical block 5, lost async page write
> > [ 21.200708] I/O error, dev mmcblk0, sector 1000873 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> > [ 21.201043] I/O error, dev mmcblk0, sector 1000929 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> > [ 21.218287] I/O error, dev mmcblk0, sector 1000945 op 0x1:(WRITE) flags 0x800 phys_seg 80 prio class 2
> > [ 21.218623] I/O error, dev mmcblk0, sector 1001033 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
> > [ 21.236813] I/O error, dev mmcblk0, sector 1001097 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> > [ 21.237146] I/O error, dev mmcblk0, sector 1001121 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
> > [ 21.255370] I/O error, dev mmcblk0, sector 1001161 op 0x1:(WRITE) flags 0x800 phys_seg 56 prio class 2
> > [ 21.256253] I/O error, dev mmcblk0, sector 1001249 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
> > ```
> >
> > Specifically it seems to be writing 0x0 to DWMCI_HS400_DQS_EN that is
> > the issue, if I comment out that line it works also for my odroid-xu4
> > in non-hs400 mode.
> >
> > Tested on odroid-xu4 with odroid-xu3_defconfig after applying the
> > entire series, without any other changes.
> >
> > Best regards,
> > Henrik Grimler
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support
2026-05-01 6:58 ` Henrik Grimler
@ 2026-05-01 9:02 ` Kaustabh Chakraborty
0 siblings, 0 replies; 10+ messages in thread
From: Kaustabh Chakraborty @ 2026-05-01 9:02 UTC (permalink / raw)
To: Henrik Grimler, Kaustabh Chakraborty
Cc: Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung,
Anand Moon, Sam Protsenko, Lukas Timmermann
On 2026-05-01 08:58 +02:00, Henrik Grimler wrote:
> Hi Kaustabh,
>
> On Fri, May 01, 2026 at 01:11:29AM +0530, Kaustabh Chakraborty wrote:
>> On 2026-04-30 19:26 +02:00, Henrik Grimler wrote:
>> > Hi Kaustabh,
>> >
>> > On Mon, Apr 27, 2026 at 11:22:49AM +0530, Kaustabh Chakraborty wrote:
>> >> HS400 support was added, but configuration necessary for HS400 support
>> >> was left out. Add necessary changes, which includes:
>> >> - Device tree properties, such as "samsung,dw-mshc-hs400-timing" and
>> >> "samsung,read-strobe-delay", which function as per dt-bindings.
>> >> - Registers related to HS400, which are necessary to enable HS400+ support.
>> >> - Appropriate timing tunings for the HS400 mode.
>> >>
>> >> Note that these changes are loosely based off of its Linux kernel
>> >> counterpart.
>> >>
>> >> Fixes: bbe3b9fa0922 ("mmc: exynos_dw_mmc: add support for MMC HS200 and HS400 modes")
>> >> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
>> >> ---
>> >> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++
>> >> drivers/mmc/exynos_dw_mmc.c | 80 +++++++++++++++++++++++++++++++
>> >> 2 files changed, 85 insertions(+)
>> >>
>> >> diff --git a/arch/arm/mach-exynos/include/mach/dwmmc.h b/arch/arm/mach-exynos/include/mach/dwmmc.h
>> >> index 4432deedef7..50081326c25 100644
>> >> --- a/arch/arm/mach-exynos/include/mach/dwmmc.h
>> >> +++ b/arch/arm/mach-exynos/include/mach/dwmmc.h
>> >> @@ -15,6 +15,11 @@
>> >> #define DWMCI_SET_DRV_CLK(x) ((x) << 16)
>> >> #define DWMCI_SET_DIV_RATIO(x) ((x) << 24)
>> >>
>> >> +/* HS400 Related Registers */
>> >> +#define DWMCI_HS400_DQS_EN 0x180
>> >> +#define DWMCI_HS400_ASYNC_FIFO_CTRL 0x184
>> >> +#define DWMCI_HS400_DLINE_CTRL 0x188
>> >> +
>> >> /* Protector Register */
>> >> #define DWMCI_EMMCP_BASE 0x1000
>> >> #define EMMCP_MPSECURITY (DWMCI_EMMCP_BASE + 0x0010)
>> >> diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
>> >> index 7ccd113bd79..d5e90a9bd5c 100644
>> >> --- a/drivers/mmc/exynos_dw_mmc.c
>> >> +++ b/drivers/mmc/exynos_dw_mmc.c
>> >> @@ -8,6 +8,7 @@
>> >> #include <dwmmc.h>
>> >> #include <asm/global_data.h>
>> >> #include <malloc.h>
>> >> +#include <mmc.h>
>> >> #include <errno.h>
>> >> #include <asm/arch/dwmmc.h>
>> >> #include <asm/arch/clk.h>
>> >> @@ -30,6 +31,14 @@
>> >> #define CLKSEL_UP_SAMPLE(x, y) (((x) & ~CLKSEL_CCLK_SAMPLE(7)) | \
>> >> CLKSEL_CCLK_SAMPLE(y))
>> >>
>> >> +/* RCLK_EN register defines */
>> >> +#define DATA_STROBE_EN BIT(0)
>> >> +#define AXI_NON_BLOCKING_WR BIT(7)
>> >> +
>> >> +/* DLINE_CTRL register defines */
>> >> +#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF))
>> >> +#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF)
>> >> +
>> >> /**
>> >> * DOC: Quirk flags for different Exynos DW MMC blocks
>> >> *
>> >> @@ -71,6 +80,11 @@ struct dwmci_exynos_priv_data {
>> >> struct clk clk;
>> >> u32 sdr_timing;
>> >> u32 ddr_timing;
>> >> + u32 hs400_timing;
>> >> + u32 tuned_sample;
>> >> + u32 dqs_delay;
>> >> + u32 saved_dqs_en;
>> >> + u32 saved_strobe_ctrl;
>> >> const struct exynos_dwmmc_variant *chip;
>> >> };
>> >>
>> >> @@ -162,6 +176,27 @@ static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host)
>> >> & DWMCI_DIVRATIO_MASK) + 1;
>> >> }
>> >>
>> >> +static void exynos_config_hs400(struct dwmci_host *host, enum bus_mode mode)
>> >> +{
>> >> + struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
>> >> + u32 dqs, strobe;
>> >> +
>> >> + dqs = priv->saved_dqs_en;
>> >> + strobe = priv->saved_strobe_ctrl;
>> >> +
>> >> + switch (mode) {
>> >> + case MMC_HS_400:
>> >> + dqs |= DATA_STROBE_EN;
>> >> + strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
>> >> + break;
>> >> + default:
>> >> + dqs &= ~DATA_STROBE_EN;
>> >> + }
>> >> +
>> >> + dwmci_writel(host, DWMCI_HS400_DQS_EN, dqs);
>> >> + dwmci_writel(host, DWMCI_HS400_DLINE_CTRL, strobe);
>> >> +}
>> >> +
>> >> /* Configure CLKSEL register with chosen timing values */
>> >> static int exynos_dwmci_clksel(struct dwmci_host *host)
>> >> {
>> >> @@ -170,6 +205,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
>> >> u32 timing;
>> >>
>> >> switch (host->mmc->selected_mode) {
>> >> + case MMC_HS_400:
>> >> + timing = CLKSEL_UP_SAMPLE(priv->hs400_timing, priv->tuned_sample);
>> >> + break;
>> >> case MMC_DDR_52:
>> >> timing = priv->ddr_timing;
>> >> break;
>> >> @@ -186,6 +224,8 @@ static int exynos_dwmci_clksel(struct dwmci_host *host)
>> >>
>> >> dwmci_writel(host, priv->chip->clksel, timing);
>> >>
>> >> + exynos_config_hs400(host, host->mmc->selected_mode);
>> >> +
>> >
>> > This breaks emmc in Linux for existing devices without hs400 support:
>> > exynos_config_hs400 is run no matter if the dt has the hs400
>> > parameters, meaning 0x0 is written to DWMCI_HS400_DQS_EN and
>> > DWMCI_HS400_DLINE_CTRL. U-boot does not seem to care, it loads the
>> > kernel and initramfs without issues, but Linux later gives errors:
>>
>> Is it that the same defconfig is used for two devices, one with and one
>> without HS400 support? If that's the case, guarding the call with
>> HS400_SUPPORT config options won't be a real fix.
>
> A family of devices are using the same defconfig. I assume the
> hardware in all of them supports hs400, but at the moment only one
> device seems to be configured to use hs400 _in Linux_, and none in
> u-boot.
It won't. If one of the devices supports HS400 and others don't (not
specifically in your case, but a general scenario), then the config
would have HS400 support enabled. As a result non-HS400 hardware would
also try to assume HS400.
>
> Adding a config guard around exynos_config_hs400() would probably
> work. I see Linux uses a check like
> if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420)
> instead.
>
>> mmc_of_parse() in mmc-uclass.h seems promising, this needs to be
>> integrated with dwmci_setup_cfg() in dw_mmc.c, and this change _may_ be
>> regressive in nature, so I may also need to add some safety guards for
>> this.
>>
>> I will thus add this patch in the next rev. Would you be up to test the patch
>> for the concerned devices too?
>
> Yeah I can test on these older devices.
>
> Best regards,
> Henrik Grimler
>
>> >
>> > ```
>> > [ 20.741025] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 400000Hz, actual 396825HZ div = 63)
>> > [ 20.886686] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
>> > [ 20.895138] mmc_host mmc0: Bus speed (slot 0) = 200000000Hz (slot req 200000000Hz, actual 200000000HZ div = 0)
>> > [ 20.905990] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 52000000Hz, actual 50000000HZ div = 0)
>> > [ 20.914988] mmc_host mmc0: Bus speed (slot 0) = 400000000Hz (slot req 200000000Hz, actual 200000000HZ div = 1)
>> > [ 21.099546] I/O error, dev mmcblk0, sector 1000001 op 0x1:(WRITE) flags 0x800 phys_seg 16 prio class 2
>> > [ 21.099853] I/O error, dev mmcblk0, sector 1000825 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
>> > [ 21.107443] Buffer I/O error on dev mmcblk0p2, logical block 0, lost async page write
>> > [ 21.107473] Buffer I/O error on dev mmcblk0p2, logical block 1, lost async page write
>> > [ 21.116657] Buffer I/O error on dev mmcblk0p2, logical block 824, lost async page write
>> > [ 21.124408] Buffer I/O error on dev mmcblk0p2, logical block 2, lost async page write
>> > [ 21.132206] Buffer I/O error on dev mmcblk0p2, logical block 825, lost async page write
>> > [ 21.140178] Buffer I/O error on dev mmcblk0p2, logical block 3, lost async page write
>> > [ 21.147973] Buffer I/O error on dev mmcblk0p2, logical block 826, lost async page write
>> > [ 21.155960] Buffer I/O error on dev mmcblk0p2, logical block 4, lost async page write
>> > [ 21.163749] Buffer I/O error on dev mmcblk0p2, logical block 827, lost async page write
>> > [ 21.171720] Buffer I/O error on dev mmcblk0p2, logical block 5, lost async page write
>> > [ 21.200708] I/O error, dev mmcblk0, sector 1000873 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
>> > [ 21.201043] I/O error, dev mmcblk0, sector 1000929 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
>> > [ 21.218287] I/O error, dev mmcblk0, sector 1000945 op 0x1:(WRITE) flags 0x800 phys_seg 80 prio class 2
>> > [ 21.218623] I/O error, dev mmcblk0, sector 1001033 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
>> > [ 21.236813] I/O error, dev mmcblk0, sector 1001097 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
>> > [ 21.237146] I/O error, dev mmcblk0, sector 1001121 op 0x1:(WRITE) flags 0x800 phys_seg 24 prio class 2
>> > [ 21.255370] I/O error, dev mmcblk0, sector 1001161 op 0x1:(WRITE) flags 0x800 phys_seg 56 prio class 2
>> > [ 21.256253] I/O error, dev mmcblk0, sector 1001249 op 0x1:(WRITE) flags 0x800 phys_seg 8 prio class 2
>> > ```
>> >
>> > Specifically it seems to be writing 0x0 to DWMCI_HS400_DQS_EN that is
>> > the issue, if I comment out that line it works also for my odroid-xu4
>> > in non-hs400 mode.
>> >
>> > Tested on odroid-xu4 with odroid-xu3_defconfig after applying the
>> > entire series, without any other changes.
>> >
>> > Best regards,
>> > Henrik Grimler
>>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-05-01 12:38 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-27 5:52 [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty
2026-04-30 17:26 ` Henrik Grimler
2026-04-30 19:41 ` Kaustabh Chakraborty
2026-05-01 6:58 ` Henrik Grimler
2026-05-01 9:02 ` Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 3/4] mmc: exynos_dw_mmc: add support for HS400ES Kaustabh Chakraborty
2026-04-27 5:52 ` [PATCH 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver Kaustabh Chakraborty
2026-04-27 20:23 ` [PATCH 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Henrik Grimler
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.