mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Johannes Schneider <johannes.schneider@leica-geosystems.com>
To: barebox@lists.infradead.org
Cc: Johannes Schneider <johannes.schneider@leica-geosystems.com>
Subject: [PATCH v1 5/7] mci: imx-esdhc: support HS400 and HS400ES on i.MX8M
Date: Sat, 27 Jun 2026 19:43:22 +0000	[thread overview]
Message-ID: <20260627194324.2230643-5-johannes.schneider@leica-geosystems.com> (raw)
In-Reply-To: <20260627194324.2230643-1-johannes.schneider@leica-geosystems.com>

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 <johannes.schneider@leica-geosystems.com>
---
 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




  parent reply	other threads:[~2026-06-27 19:45 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-27 19:43 [PATCH v1 1/7] dma: make dma_mapping_error() NULL-safe Johannes Schneider
2026-06-27 19:43 ` [PATCH v1 2/7] mci: sdhci: bail out on ADMA/transfer errors instead of hanging Johannes Schneider
2026-06-27 19:43 ` [PATCH v1 3/7] mci: sdhci: honor BROKEN_ADMA_ZEROLEN_DESC / NO_ENDATTR_IN_NOPDESC quirks Johannes Schneider
2026-06-27 19:43 ` [PATCH v1 4/7] mci: imx-esdhc: mark the uSDHC ADMA2 descriptor quirks Johannes Schneider
2026-06-27 19:43 ` Johannes Schneider [this message]
2026-06-27 19:43 ` [PATCH v1 6/7] mci: imx-esdhc: make the transfer mode selectable (PIO/SDMA/ADMA2) Johannes Schneider
2026-06-27 19:43 ` [PATCH v1 7/7] mci: imx-esdhc: support DMA in the i.MX8M PBL eMMC loader Johannes Schneider

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260627194324.2230643-5-johannes.schneider@leica-geosystems.com \
    --to=johannes.schneider@leica-geosystems.com \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox