The i.MX8M uSDHC can run HS400/HS400ES, but the barebox driver topped out at
HS200: fsl,imx8mm-usdhc was routed to usdhc_imx6sx_data (HS200 only) and the
HS400 timing/strobe-DLL path was absent.

Add a usdhc_imx8mm_data with ESDHC_FLAG_HS200 | HS400 | HS400_ES and point the
imx8mm/imx8mn/imx8mp compatibles at it. Add the strobe-DLL register block and
esdhc_set_strobe_dll() (gate the card clock, pulse reset, program the read
delay target, poll for REF/SLV lock), an HS400 case in usdhc_set_timing()
(set MIX_CTRL DDREN|HS400_EN, re-apply the clock, run the strobe DLL),
HS400ES handling, and MMC_CAP2_HS400[_ES] advertisement gated on the flags.
The MIXCTRL preserve-mask in __esdhc_send_cmd() is widened from bits 22-25 to
22-27 so the HS400 mode bits survive command writes. Strobe read delay is
configurable via the existing fsl,strobe-dll-delay-target DT binding
(default 0x7).

usdhc_imx8mm_data keeps ESDHC_FLAG_STD_TUNING: barebox already drove these
parts (fsl,imx8mm-usdhc) at HS200 with standard tuning, and U-Boot uses it for
the i.MX8M family too. (Linux uses manual tuning for imx8mm; moving barebox to
manual tuning would be a separate, separately-tested change.)

Register sequence mirrors Linux sdhci-esdhc-imx.c; the barebox idiom follows
the Rockchip dwcmshc HS400 support.

Assisted-by: Claude Opus 4.8 (1M context)
Signed-off-by: Johannes Schneider <[email protected]>
---

Notes:
    v2:
    - No functional change; rebased on next.

 drivers/mci/imx-esdhc-common.c |  4 +-
 drivers/mci/imx-esdhc.c        | 91 ++++++++++++++++++++++++++++++++--
 drivers/mci/imx-esdhc.h        | 14 ++++++
 3 files changed, 103 insertions(+), 6 deletions(-)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index d31a2ad779..a0c2dee32d 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -360,8 +360,8 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct 
mci_cmd *cmd,
        if (esdhc_is_usdhc(host)) {
                /* write lower-half of xfertyp to mixctrl */
                mixctrl = xfertyp;
-               /* Keep the bits 22-25 of the register as is */
-               mixctrl |= (sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL) & 
(0xF << 22));
+               /* keep tuning bits (22-25) and HS400 mode bits (26-27) as is */
+               mixctrl |= (sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL) & 
(0x3F << 22));
                mixctrl |= mci_timing_is_ddr(host->sdhci.timing) ? 
MIX_CTRL_DDREN : 0;
                sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
        }
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index ba33daffc4..e123bd3ede 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -274,12 +274,51 @@ static int esdhc_change_pinstate(struct fsl_esdhc_host 
*host,
 }
 
 
+/* Ported from Linux esdhc_set_strobe_dll() in 
drivers/mmc/host/sdhci-esdhc-imx.c */
+static void esdhc_set_strobe_dll(struct fsl_esdhc_host *host)
+{
+       u32 strobe_delay, v;
+       int ret;
+
+       /* disable the card clock before (re)programming the strobe DLL */
+       sdhci_write32(&host->sdhci, ESDHC_VENDOR_SPEC,
+                     sdhci_read32(&host->sdhci, ESDHC_VENDOR_SPEC) &
+                     ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON);
+       ret = esdhc_poll(host, ESDHC_PRSSTAT, v, (v & ESDHC_CLOCK_GATE_OFF),
+                        100 * USECOND);
+       if (ret)
+               dev_warn(host->dev, "card clock not gated off before strobe 
DLL\n");
+
+       /* force a reset on the strobe DLL, then clear it before any setting */
+       sdhci_write32(&host->sdhci, ESDHC_STROBE_DLL_CTRL,
+                     ESDHC_STROBE_DLL_CTRL_RESET);
+       sdhci_write32(&host->sdhci, ESDHC_STROBE_DLL_CTRL, 0);
+
+       if (host->boarddata.strobe_dll_delay_target)
+               strobe_delay = host->boarddata.strobe_dll_delay_target;
+       else
+               strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
+
+       v = ESDHC_STROBE_DLL_CTRL_ENABLE |
+           ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
+           (strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
+       sdhci_write32(&host->sdhci, ESDHC_STROBE_DLL_CTRL, v);
+
+       ret = esdhc_poll(host, ESDHC_STROBE_DLL_STATUS, v,
+                        (v & ESDHC_STROBE_DLL_STS_REF_LOCK) &&
+                        (v & ESDHC_STROBE_DLL_STS_SLV_LOCK),
+                        50 * USECOND);
+       if (ret)
+               dev_warn(host->dev,
+                        "HS400 strobe DLL not locked in 50us, status 
0x%08x\n", v);
+}
+
 static void usdhc_set_timing(struct fsl_esdhc_host *host, enum mci_timing 
timing)
 {
        u32 mixctrl;
 
        mixctrl = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
-       mixctrl &= ~MIX_CTRL_DDREN;
+       mixctrl &= ~(MIX_CTRL_DDREN | MIX_CTRL_HS400_EN);
 
        switch (timing) {
        case MMC_TIMING_UHS_DDR50:
@@ -296,6 +335,14 @@ static void usdhc_set_timing(struct fsl_esdhc_host *host, 
enum mci_timing timing
                        sdhci_write32(&host->sdhci, IMX_SDHCI_DLL_CTRL, v);
                }
                break;
+       case MMC_TIMING_MMC_HS400:
+               /*
+                * HS400 is a DDR mode; the strobe DLL is set up later from
+                * esdhc_set_ios() once the HS400 clock is running.
+                */
+               mixctrl |= MIX_CTRL_DDREN | MIX_CTRL_HS400_EN;
+               sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+               break;
        case MMC_TIMING_UHS_SDR12:
        case MMC_TIMING_UHS_SDR25:
        case MMC_TIMING_UHS_SDR50:
@@ -339,6 +386,19 @@ static void layerscape_set_timing(struct fsl_esdhc_host 
*host, enum mci_timing t
        host->sdhci.timing = timing;
 }
 
+static void esdhc_hs400_enhanced_strobe(struct mci_host *mci, struct mci_ios 
*ios)
+{
+       struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
+       u32 m;
+
+       m = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
+       if (ios->enhanced_strobe)
+               m |= MIX_CTRL_HS400_ES;
+       else
+               m &= ~MIX_CTRL_HS400_ES;
+       sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, m);
+}
+
 static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
 {
        struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
@@ -386,6 +446,13 @@ static void esdhc_set_ios(struct mci_host *mci, struct 
mci_ios *ios)
                return;
        }
 
+       /*
+        * (Re)program the strobe DLL on every HS400 set_ios(): mci-core selects
+        * HS400 timing while still at the old clock and only raises the clock 
on
+        * a later set_ios(), so the DLL must lock against the final read clock.
+        */
+       if (ios->timing == MMC_TIMING_MMC_HS400 && ios->clock)
+               esdhc_set_strobe_dll(host);
 }
 
 static int esdhc_card_present(struct mci_host *mci)
@@ -511,6 +578,7 @@ static const struct mci_ops fsl_esdhc_ops = {
        .set_ios = esdhc_set_ios,
        .init = esdhc_init,
        .card_present = esdhc_card_present,
+       .hs400_enhanced_strobe = esdhc_hs400_enhanced_strobe,
 };
 
 static void fsl_esdhc_probe_dt(struct device *dev, struct fsl_esdhc_host *host)
@@ -528,6 +596,9 @@ static void fsl_esdhc_probe_dt(struct device *dev, struct 
fsl_esdhc_host *host)
                boarddata->tuning_start_tap = ESDHC_TUNING_START_TAP_DEFAULT;
        if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
                boarddata->delay_line = 0;
+       if (of_property_read_u32(np, "fsl,strobe-dll-delay-target",
+                            &boarddata->strobe_dll_delay_target))
+               boarddata->strobe_dll_delay_target = 0;
 
        if (esdhc_is_usdhc(host) && !IS_ERR(host->pinctrl)) {
                host->pins_100mhz = pinctrl_lookup_state(host->pinctrl,
@@ -567,6 +638,11 @@ static bool usdhc_setup_tuning(struct fsl_esdhc_host *host)
        mci->ops.execute_tuning = usdhc_execute_tuning;
        mci->caps2 |= MMC_CAP2_HS200;
 
+       if (host->socdata->flags & ESDHC_FLAG_HS400)
+               mci->caps2 |= MMC_CAP2_HS400_1_8V;
+       if (host->socdata->flags & ESDHC_FLAG_HS400_ES)
+               mci->caps2 |= MMC_CAP2_HS400_ES;
+
        return true;
 }
 
@@ -684,6 +760,13 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
        .clkidx = "per",
 };
 
+static struct esdhc_soc_data usdhc_imx8mm_data = {
+       .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+              | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
+              | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES,
+       .clkidx = "per",
+};
+
 static struct esdhc_soc_data esdhc_ls_be_data = {
        .flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_BIGENDIAN |
                 ESDHC_FLAG_LAYERSCAPE,
@@ -702,9 +785,9 @@ static __maybe_unused struct of_device_id 
fsl_esdhc_compatible[] = {
        { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data },
        { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data },
        { .compatible = "fsl,imx8mq-usdhc", .data = &usdhc_imx6sx_data },
-       { .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx6sx_data },
-       { .compatible = "fsl,imx8mn-usdhc", .data = &usdhc_imx6sx_data },
-       { .compatible = "fsl,imx8mp-usdhc", .data = &usdhc_imx6sx_data },
+       { .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data },
+       { .compatible = "fsl,imx8mn-usdhc", .data = &usdhc_imx8mm_data },
+       { .compatible = "fsl,imx8mp-usdhc", .data = &usdhc_imx8mm_data },
        { .compatible = "fsl,ls1028a-esdhc",.data = &esdhc_ls_le_data  },
        { .compatible = "fsl,ls1046a-esdhc",.data = &esdhc_ls_be_data  },
        { /* sentinel */ }
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index df8aca18ad..5562371652 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -66,6 +66,17 @@
 #define  IMX_SDHCI_DLL_CTRL_OVERRIDE_EN_SHIFT  8
 #define IMX_SDHCI_MIX_CTRL_FBCLK_SEL   BIT(25)
 
+/* HS400 data strobe line delay-locked loop (read clock) */
+#define ESDHC_STROBE_DLL_CTRL                          0x70
+#define  ESDHC_STROBE_DLL_CTRL_ENABLE                  BIT(0)
+#define  ESDHC_STROBE_DLL_CTRL_RESET                   BIT(1)
+#define  ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT    3
+#define  ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT  0x7
+#define  ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT  (4 << 20)
+#define ESDHC_STROBE_DLL_STATUS                                0x74
+#define  ESDHC_STROBE_DLL_STS_REF_LOCK                 BIT(1)
+#define  ESDHC_STROBE_DLL_STS_SLV_LOCK                 BIT(0)
+
 /* pltfm-specific */
 #define ESDHC_HOST_CONTROL_LE  0x20
 
@@ -163,6 +174,8 @@
 #define ESDHC_FLAG_BIGENDIAN           BIT(10)
 /* Layerscape variant ls1046a, ls1028a, ls1088a, revisit for ls1012a */
 #define ESDHC_FLAG_LAYERSCAPE          BIT(11)
+/* The IP supports HS400ES mode */
+#define ESDHC_FLAG_HS400_ES            BIT(12)
 
 struct esdhc_soc_data {
        u32 flags;
@@ -177,6 +190,7 @@ struct esdhc_platform_data {
        unsigned int delay_line;
        unsigned int tuning_step;       /* The delay cell steps in tuning 
procedure */
        unsigned int tuning_start_tap;  /* The start delay cell point in tuning 
procedure */
+       unsigned int strobe_dll_delay_target;   /* HS400 strobe read-clock 
delay cell */
 };
 
 struct fsl_esdhc_host {
-- 
2.43.0


Reply via email to