* [PATCH v2 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers
@ 2026-05-03 12:21 Kaustabh Chakraborty
2026-05-03 12:21 ` [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty
` (3 more replies)
0 siblings, 4 replies; 12+ messages in thread
From: Kaustabh Chakraborty @ 2026-05-03 12:21 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>
---
Changes in v2:
- added config guard for calls to exynos_config_hs400() (Henrik Grimler)
- Link to v1: https://patch.msgid.link/20260427-dwmmc-exynos-hs400-es-v1-0-3495df40a9ac@disroot.org
To: Peng Fan <peng.fan@nxp.com>
To: Kaustabh Chakraborty <kauschluss@disroot.org>
To: u-boot@lists.denx.de
Cc: Minkyu Kang <mk7.kang@samsung.com>
Cc: Tom Rini <trini@konsulko.com>
Cc: Jaehoon Chung <jh80.chung@samsung.com>
Cc: Sam Protsenko <semen.protsenko@linaro.org>
Cc: Anand Moon <linux.amoon@gmail.com>
Cc: Lukas Timmermann <uboot@timmermann.space>
Cc: Henrik Grimler <henrik@grimler.se>
---
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 | 90 ++++++++++++++++++++++++++++++-
include/dwmmc.h | 10 ++++
5 files changed, 123 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] 12+ messages in thread* [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-03 12:21 [PATCH v2 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty @ 2026-05-03 12:21 ` Kaustabh Chakraborty 2026-05-05 19:07 ` Henrik Grimler 2026-05-03 12:21 ` [PATCH v2 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver Kaustabh Chakraborty ` (2 subsequent siblings) 3 siblings, 1 reply; 12+ messages in thread From: Kaustabh Chakraborty @ 2026-05-03 12:21 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 | 81 +++++++++++++++++++++++++++++++ 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) dwmci_writel(host, priv->chip->clksel, timing); + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) + exynos_config_hs400(host, host->mmc->selected_mode); + return 0; } @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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] 12+ messages in thread
* Re: [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-03 12:21 ` [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty @ 2026-05-05 19:07 ` Henrik Grimler 2026-05-06 20:03 ` Kaustabh Chakraborty 2026-05-13 10:58 ` Peng Fan 0 siblings, 2 replies; 12+ messages in thread From: Henrik Grimler @ 2026-05-05 19:07 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 Sun, May 03, 2026 at 05:51:26PM +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> Reviewed-by: Henrik Grimler <henrik@grimler.se> This works fine on exynos5422-odroid-xu4 without hs400. I was not able to get hs400 working on the device, seems more changes than just dts update are needed. Best regards, Henrik Grimler > --- > arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++ > drivers/mmc/exynos_dw_mmc.c | 81 +++++++++++++++++++++++++++++++ > 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) > > dwmci_writel(host, priv->chip->clksel, timing); > > + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) > + exynos_config_hs400(host, host->mmc->selected_mode); > + > return 0; > } > > @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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 [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-05 19:07 ` Henrik Grimler @ 2026-05-06 20:03 ` Kaustabh Chakraborty 2026-05-07 19:24 ` Henrik Grimler 2026-05-13 10:58 ` Peng Fan 1 sibling, 1 reply; 12+ messages in thread From: Kaustabh Chakraborty @ 2026-05-06 20:03 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-05 21:07 +02:00, Henrik Grimler wrote: > Hi Kaustabh, > > On Sun, May 03, 2026 at 05:51:26PM +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> > > Reviewed-by: Henrik Grimler <henrik@grimler.se> > > This works fine on exynos5422-odroid-xu4 without hs400. I was not able > to get hs400 working on the device, seems more changes than just dts > update are needed. Do you have any logs or traces which may be able to help pinpoint it? I can try to figure out the issue(s) if you want me to. > > Best regards, > Henrik Grimler > >> --- >> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++ >> drivers/mmc/exynos_dw_mmc.c | 81 +++++++++++++++++++++++++++++++ >> 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) >> >> dwmci_writel(host, priv->chip->clksel, timing); >> >> + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) >> + exynos_config_hs400(host, host->mmc->selected_mode); >> + >> return 0; >> } >> >> @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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 [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-06 20:03 ` Kaustabh Chakraborty @ 2026-05-07 19:24 ` Henrik Grimler 2026-05-08 15:27 ` Anand Moon 0 siblings, 1 reply; 12+ messages in thread From: Henrik Grimler @ 2026-05-07 19:24 UTC (permalink / raw) To: Kaustabh Chakraborty Cc: Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko, Lukas Timmermann [-- Attachment #1: Type: text/plain, Size: 8794 bytes --] Hi Kaustabh, On Thu, May 07, 2026 at 01:33:28AM +0530, Kaustabh Chakraborty wrote: > On 2026-05-05 21:07 +02:00, Henrik Grimler wrote: > > Hi Kaustabh, > > > > On Sun, May 03, 2026 at 05:51:26PM +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> > > > > Reviewed-by: Henrik Grimler <henrik@grimler.se> > > > > This works fine on exynos5422-odroid-xu4 without hs400. I was not able > > to get hs400 working on the device, seems more changes than just dts > > update are needed. > > Do you have any logs or traces which may be able to help pinpoint it? > I can try to figure out the issue(s) if you want me to. I fully synced the mmc dts node with linux variant. This device does not have a pinctrl driver so pinctrl settings are not applied though, so I also updated the exynos-pinmux mmc function. See attached diff. I then also enabled just CONFIG_MMC_HS400_SUPPORT, or both MMC_HS400_SUPPORT and MMC_HS400_ES_SUPPORT. With MMC_HS400_ES_SUPPORT I get: ODROID-XU3 # mmc info Select HS400ES failed -22 unable to select a mode: -5 And with just MMC_HS400_SUPPORT I get: ODROID-XU3 # mmc info Select HS400 failed -110 unable to select a mode: -110 I have not run it with additional debugging. I have plans to switch over to OF_UPSTREAM, and add a pinctrl driver, maybe that would fix it. Probably not worth spending too much time debugging it at this point. Best regards, Henrik Grimler > > > > Best regards, > > Henrik Grimler > > > >> --- > >> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++ > >> drivers/mmc/exynos_dw_mmc.c | 81 +++++++++++++++++++++++++++++++ > >> 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) > >> > >> dwmci_writel(host, priv->chip->clksel, timing); > >> > >> + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) > >> + exynos_config_hs400(host, host->mmc->selected_mode); > >> + > >> return 0; > >> } > >> > >> @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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 > >> > [-- Attachment #2: odroid-xu4-hs400.diff --] [-- Type: text/plain, Size: 3505 bytes --] diff --git a/arch/arm/dts/exynos5422-odroidxu3.dts b/arch/arm/dts/exynos5422-odroidxu3.dts index e147fcb8643b..8d9f4470d890 100644 --- a/arch/arm/dts/exynos5422-odroidxu3.dts +++ b/arch/arm/dts/exynos5422-odroidxu3.dts @@ -279,10 +280,6 @@ status = "okay"; }; - mmc@12200000 { - fifo-depth = <0x40>; - }; - mmc@12220000 { fifo-depth = <0x40>; }; @@ -292,3 +289,76 @@ reset-gpio = <&gpd1 0 0>; }; }; + +&mmc0 { + fifo-depth = <0x40>; + status = "okay"; + // mmc-pwrseq = <&emmc_pwrseq>; + card-detect-delay = <200>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 4>; + samsung,dw-mshc-ddr-timing = <0 2>; + samsung,dw-mshc-hs400-timing = <0 2>; + samsung,read-strobe-delay = <90>; + pinctrl-names = "default"; + pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus1 &sd0_bus4 &sd0_bus8 &sd0_cd &sd0_rclk>; + bus-width = <8>; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + max-frequency = <200000000>; + vmmc-supply = <&ldo18_reg>; + vqmmc-supply = <&ldo3_reg>; +}; + +&pinctrl_1 { + sd0_clk: sd0-clk-pins { + samsung,pins = "gpc0-0"; + samsung,pin-function = <EXYNOS_PIN_FUNC_2>; + samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>; + samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>; + }; + + sd0_cmd: sd0-cmd-pins { + samsung,pins = "gpc0-1"; + samsung,pin-function = <EXYNOS_PIN_FUNC_2>; + samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>; + samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>; + }; + + sd0_cd: sd0-cd-pins { + samsung,pins = "gpc0-2"; + samsung,pin-function = <EXYNOS_PIN_FUNC_2>; + samsung,pin-pud = <EXYNOS_PIN_PULL_UP>; + samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>; + }; + + sd0_bus1: sd0-bus-width1-pins { + samsung,pins = "gpc0-3"; + samsung,pin-function = <EXYNOS_PIN_FUNC_2>; + samsung,pin-pud = <EXYNOS_PIN_PULL_UP>; + samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>; + }; + + sd0_bus4: sd0-bus-width4-pins { + samsung,pins = "gpc0-4", "gpc0-5", "gpc0-6"; + samsung,pin-function = <EXYNOS_PIN_FUNC_2>; + samsung,pin-pud = <EXYNOS_PIN_PULL_UP>; + samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>; + }; + + sd0_bus8: sd0-bus-width8-pins { + samsung,pins = "gpc3-0", "gpc3-1", "gpc3-2", "gpc3-3"; + samsung,pin-function = <EXYNOS_PIN_FUNC_2>; + samsung,pin-pud = <EXYNOS_PIN_PULL_UP>; + samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>; + }; + + sd0_rclk: sd0-rclk-pins { + samsung,pins = "gpc0-7"; + samsung,pin-function = <EXYNOS_PIN_FUNC_2>; + samsung,pin-pud = <EXYNOS_PIN_PULL_DOWN>; + samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>; + }; +}; diff --git a/arch/arm/dts/exynos54xx.dtsi b/arch/arm/dts/exynos54xx.dtsi index 5915ed697791..6f8999a3c506 100644 --- a/arch/arm/dts/exynos54xx.dtsi +++ b/arch/arm/dts/exynos54xx.dtsi @@ -118,12 +118,8 @@ samsung,i2s-id = <0>; }; - mmc@12200000 { + mmc0: mmc@12200000 { bus-width = <8>; - samsung,dw-mshc-ciu-div = <3>; - samsung,dw-mshc-sdr-timing = <1 3>; - non-removable; - samsung,pre-init; }; mmc@12210000 { diff --git a/arch/arm/mach-exynos/pinmux.c b/arch/arm/mach-exynos/pinmux.c index ed46ea033558..d62f31304413 100644 --- a/arch/arm/mach-exynos/pinmux.c +++ b/arch/arm/mach-exynos/pinmux.c @@ -111,6 +111,9 @@ static int exynos5_mmc_config(int peripheral, int flags) gpio_set_pull(i, S5P_GPIO_PULL_UP); gpio_set_drv(i, S5P_GPIO_DRV_4X); } + gpio_cfg_pin(start+7, S5P_GPIO_FUNC(0x2)); + gpio_set_pull(start+7, S5P_GPIO_PULL_DOWN); + gpio_set_drv(start+7, S5P_GPIO_DRV_4X); } for (i = start; i < (start + 2); i++) { gpio_cfg_pin(i, S5P_GPIO_FUNC(0x2)); ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-07 19:24 ` Henrik Grimler @ 2026-05-08 15:27 ` Anand Moon 0 siblings, 0 replies; 12+ messages in thread From: Anand Moon @ 2026-05-08 15:27 UTC (permalink / raw) To: Henrik Grimler Cc: Kaustabh Chakraborty, Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung, Sam Protsenko, Lukas Timmermann Hi Henrik, On Fri, 8 May 2026 at 00:55, Henrik Grimler <henrik@grimler.se> wrote: > > Hi Kaustabh, > > On Thu, May 07, 2026 at 01:33:28AM +0530, Kaustabh Chakraborty wrote: > > On 2026-05-05 21:07 +02:00, Henrik Grimler wrote: > > > Hi Kaustabh, > > > > > > On Sun, May 03, 2026 at 05:51:26PM +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> > > > > > > Reviewed-by: Henrik Grimler <henrik@grimler.se> > > > > > > This works fine on exynos5422-odroid-xu4 without hs400. I was not able > > > to get hs400 working on the device, seems more changes than just dts > > > update are needed. > > > > Do you have any logs or traces which may be able to help pinpoint it? > > I can try to figure out the issue(s) if you want me to. > > I fully synced the mmc dts node with linux variant. This device does > not have a pinctrl driver so pinctrl settings are not applied though, > so I also updated the exynos-pinmux mmc function. See attached diff. > > I then also enabled just CONFIG_MMC_HS400_SUPPORT, or both > MMC_HS400_SUPPORT and MMC_HS400_ES_SUPPORT. With MMC_HS400_ES_SUPPORT > I get: > > ODROID-XU3 # mmc info > Select HS400ES failed -22 > unable to select a mode: -5 > > And with just MMC_HS400_SUPPORT I get: > > ODROID-XU3 # mmc info > Select HS400 failed -110 > unable to select a mode: -110 > > I have not run it with additional debugging. > > I have plans to switch over to OF_UPSTREAM, and add a pinctrl driver, > maybe that would fix it. Probably not worth spending too much time > debugging it at this point. > We need to transition Exynos4 and Exynos5 SoCs to OF_UPSTREAM. I attempted this earlier, but the clock subsystem for these SoCs still requires work to function correctly in U-Boot. Thanks -Anand > Best regards, > Henrik Grimler > > > > > > > Best regards, > > > Henrik Grimler > > > > > >> --- > > >> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++ > > >> drivers/mmc/exynos_dw_mmc.c | 81 +++++++++++++++++++++++++++++++ > > >> 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) > > >> > > >> dwmci_writel(host, priv->chip->clksel, timing); > > >> > > >> + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) > > >> + exynos_config_hs400(host, host->mmc->selected_mode); > > >> + > > >> return 0; > > >> } > > >> > > >> @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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 [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-05 19:07 ` Henrik Grimler 2026-05-06 20:03 ` Kaustabh Chakraborty @ 2026-05-13 10:58 ` Peng Fan 2026-05-13 12:52 ` Kaustabh Chakraborty 1 sibling, 1 reply; 12+ messages in thread From: Peng Fan @ 2026-05-13 10:58 UTC (permalink / raw) To: Henrik Grimler, Kaustabh Chakraborty Cc: Kaustabh Chakraborty, Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko, Lukas Timmermann On Tue, May 05, 2026 at 09:07:29PM +0200, Henrik Grimler wrote: >Hi Kaustabh, > >On Sun, May 03, 2026 at 05:51:26PM +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> > >Reviewed-by: Henrik Grimler <henrik@grimler.se> > >This works fine on exynos5422-odroid-xu4 without hs400. I was not able >to get hs400 working on the device, seems more changes than just dts >update are needed. Is this patchset good for you all or is there any plan for a new version for 2026.07? Thanks Peng > >Best regards, >Henrik Grimler > >> --- >> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++ >> drivers/mmc/exynos_dw_mmc.c | 81 +++++++++++++++++++++++++++++++ >> 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) >> >> dwmci_writel(host, priv->chip->clksel, timing); >> >> + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) >> + exynos_config_hs400(host, host->mmc->selected_mode); >> + >> return 0; >> } >> >> @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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 [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-13 10:58 ` Peng Fan @ 2026-05-13 12:52 ` Kaustabh Chakraborty 2026-05-13 13:03 ` Henrik Grimler 0 siblings, 1 reply; 12+ messages in thread From: Kaustabh Chakraborty @ 2026-05-13 12:52 UTC (permalink / raw) To: Peng Fan, Henrik Grimler, Kaustabh Chakraborty Cc: Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko, Lukas Timmermann On 2026-05-13 18:58 +08:00, Peng Fan wrote: > On Tue, May 05, 2026 at 09:07:29PM +0200, Henrik Grimler wrote: >>Hi Kaustabh, >> >>On Sun, May 03, 2026 at 05:51:26PM +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> >> >>Reviewed-by: Henrik Grimler <henrik@grimler.se> >> >>This works fine on exynos5422-odroid-xu4 without hs400. I was not able >>to get hs400 working on the device, seems more changes than just dts >>update are needed. > > Is this patchset good for you all or is there any plan for a new version for > 2026.07? It's fine by me. It also works with odroid-xu4 mainline u-boot (which does not enable HS400/ES in its config), so it's fine to be pulled in. > > Thanks > Peng > >> >>Best regards, >>Henrik Grimler >> >>> --- >>> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++ >>> drivers/mmc/exynos_dw_mmc.c | 81 +++++++++++++++++++++++++++++++ >>> 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) >>> >>> dwmci_writel(host, priv->chip->clksel, timing); >>> >>> + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) >>> + exynos_config_hs400(host, host->mmc->selected_mode); >>> + >>> return 0; >>> } >>> >>> @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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 [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support 2026-05-13 12:52 ` Kaustabh Chakraborty @ 2026-05-13 13:03 ` Henrik Grimler 0 siblings, 0 replies; 12+ messages in thread From: Henrik Grimler @ 2026-05-13 13:03 UTC (permalink / raw) To: Peng Fan, Kaustabh Chakraborty Cc: Peng Fan, Peng Fan, u-boot, Minkyu Kang, Tom Rini, Jaehoon Chung, Anand Moon, Sam Protsenko, Lukas Timmermann Hi, On Wed, May 13, 2026 at 06:22:54PM +0530, Kaustabh Chakraborty wrote: > On 2026-05-13 18:58 +08:00, Peng Fan wrote: > > On Tue, May 05, 2026 at 09:07:29PM +0200, Henrik Grimler wrote: > >>Hi Kaustabh, > >> > >>On Sun, May 03, 2026 at 05:51:26PM +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> > >> > >>Reviewed-by: Henrik Grimler <henrik@grimler.se> > >> > >>This works fine on exynos5422-odroid-xu4 without hs400. I was not able > >>to get hs400 working on the device, seems more changes than just dts > >>update are needed. > > > > Is this patchset good for you all or is there any plan for a new version for > > 2026.07? > > It's fine by me. It also works with odroid-xu4 mainline u-boot (which > does not enable HS400/ES in its config), so it's fine to be pulled in. I agree with Kaustabh, the patches are ready to be merged in my opinion. Best regards, Henrik Grimler > > > > Thanks > > Peng > > > >> > >>Best regards, > >>Henrik Grimler > >> > >>> --- > >>> arch/arm/mach-exynos/include/mach/dwmmc.h | 5 ++ > >>> drivers/mmc/exynos_dw_mmc.c | 81 +++++++++++++++++++++++++++++++ > >>> 2 files changed, 86 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..6558cdc803d 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,9 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) > >>> > >>> dwmci_writel(host, priv->chip->clksel, timing); > >>> > >>> + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) > >>> + exynos_config_hs400(host, host->mmc->selected_mode); > >>> + > >>> return 0; > >>> } > >>> > >>> @@ -223,6 +264,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 +370,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 +423,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 +442,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 +467,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 [flat|nested] 12+ messages in thread
* [PATCH v2 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver 2026-05-03 12:21 [PATCH v2 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty @ 2026-05-03 12:21 ` Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 3/4] mmc: exynos_dw_mmc: add support for HS400ES Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver Kaustabh Chakraborty 3 siblings, 0 replies; 12+ messages in thread From: Kaustabh Chakraborty @ 2026-05-03 12:21 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] 12+ messages in thread
* [PATCH v2 3/4] mmc: exynos_dw_mmc: add support for HS400ES 2026-05-03 12:21 [PATCH v2 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver Kaustabh Chakraborty @ 2026-05-03 12:21 ` Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver Kaustabh Chakraborty 3 siblings, 0 replies; 12+ messages in thread From: Kaustabh Chakraborty @ 2026-05-03 12:21 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 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 6558cdc803d..e0c4bbb17cc 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; @@ -224,7 +226,8 @@ static int exynos_dwmci_clksel(struct dwmci_host *host) dwmci_writel(host, priv->chip->clksel, timing); - if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) + if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) || + CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)) exynos_config_hs400(host, host->mmc->selected_mode); return 0; @@ -239,7 +242,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; } @@ -264,7 +268,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; @@ -522,7 +527,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] 12+ messages in thread
* [PATCH v2 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver 2026-05-03 12:21 [PATCH v2 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty ` (2 preceding siblings ...) 2026-05-03 12:21 ` [PATCH v2 3/4] mmc: exynos_dw_mmc: add support for HS400ES Kaustabh Chakraborty @ 2026-05-03 12:21 ` Kaustabh Chakraborty 3 siblings, 0 replies; 12+ messages in thread From: Kaustabh Chakraborty @ 2026-05-03 12:21 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] 12+ messages in thread
end of thread, other threads:[~2026-05-13 13:04 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-03 12:21 [PATCH v2 0/4] HS400 and HS400ES support for Exynos DW-MMC drivers Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 1/4] mmc: exynos_dw_mmc: add proper init sequence for HS400 support Kaustabh Chakraborty 2026-05-05 19:07 ` Henrik Grimler 2026-05-06 20:03 ` Kaustabh Chakraborty 2026-05-07 19:24 ` Henrik Grimler 2026-05-08 15:27 ` Anand Moon 2026-05-13 10:58 ` Peng Fan 2026-05-13 12:52 ` Kaustabh Chakraborty 2026-05-13 13:03 ` Henrik Grimler 2026-05-03 12:21 ` [PATCH v2 2/4] mmc: dw_mmc: setup set_enhanced_strobe ops in driver Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 3/4] mmc: exynos_dw_mmc: add support for HS400ES Kaustabh Chakraborty 2026-05-03 12:21 ` [PATCH v2 4/4] configs: exynos-mobile: enable support for HS400ES in MMC driver Kaustabh Chakraborty
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox