mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v1 1/7] dma: make dma_mapping_error() NULL-safe
@ 2026-06-27 19:43 Johannes Schneider
  2026-06-27 19:43 ` [PATCH v1 2/7] mci: sdhci: bail out on ADMA/transfer errors instead of hanging Johannes Schneider
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Johannes Schneider @ 2026-06-27 19:43 UTC (permalink / raw)
  To: barebox; +Cc: Johannes Schneider

dma_mapping_error() dereferenced dev->dma_mask unconditionally, while the
neighbouring cpu_to_dma()/dma_to_cpu() helpers already guard with 'dev &&'.
Callers with no device (e.g. the PBL SDHCI layer, which has no mci/device
yet) thus dereferenced a NULL pointer. Guard the dereference.

Assisted-by: Claude Opus 4.8 (1M context)
Signed-off-by: Johannes Schneider <johannes.schneider@leica-geosystems.com>
---
 include/dma.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/dma.h b/include/dma.h
index b8016c2e89..8f8ac78cdc 100644
--- a/include/dma.h
+++ b/include/dma.h
@@ -70,7 +70,7 @@ static inline void dma_set_mask(struct device *dev, u64 dma_mask)
 static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
 {
 	return dma_addr == DMA_ERROR_CODE ||
-		(dev->dma_mask && dma_addr > dev->dma_mask);
+		(dev && dev->dma_mask && dma_addr > dev->dma_mask);
 }
 
 static inline dma_addr_t cpu_to_dma(struct device *dev, void *cpu_addr)

base-commit: da91ab6cb505fb09415bf8df3fae552e7b37c5e1
-- 
2.43.0




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v1 2/7] mci: sdhci: bail out on ADMA/transfer errors instead of hanging
  2026-06-27 19:43 [PATCH v1 1/7] dma: make dma_mapping_error() NULL-safe Johannes Schneider
@ 2026-06-27 19:43 ` 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
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Johannes Schneider @ 2026-06-27 19:43 UTC (permalink / raw)
  To: barebox; +Cc: Johannes Schneider

sdhci_transfer_data_dma() only watched for the completion and DMA-boundary
interrupts, so a transfer/ADMA error (SDHCI_INT_ERROR, e.g. an ADMA
descriptor fault) left it spinning until the 10s timeout. Check the error
bits and abort the transfer with -EIO, dumping the ADMA error status.

Assisted-by: Claude Opus 4.8 (1M context)
Signed-off-by: Johannes Schneider <johannes.schneider@leica-geosystems.com>
---
 drivers/mci/sdhci.c | 8 ++++++++
 drivers/mci/sdhci.h | 1 +
 2 files changed, 9 insertions(+)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 3474ef129b..ccd5d4b83c 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -779,6 +779,14 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_cmd *cmd,
 			goto out;
 		}
 
+		/* e.g. ADMA error: SDHCI_INT_ERROR without a data-error bit */
+		if (irqstat & SDHCI_INT_ERROR) {
+			dev_err(dev, "transfer error: int 0x%08x adma 0x%02x\n",
+				irqstat, sdhci_read8(sdhci, SDHCI_ADMA_ERROR));
+			ret = -EIO;
+			goto out;
+		}
+
 		/*
 		 * We currently don't do anything fancy with DMA
 		 * boundaries, but as we can't disable the feature
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index d1f05ac968..558469e8ab 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -111,6 +111,7 @@
 #define  SDHCI_RESET_DATA			BIT(2)
 #define SDHCI_INT_STATUS					0x30
 #define SDHCI_INT_NORMAL_STATUS					0x30
+#define  SDHCI_INT_ADMA_ERROR			BIT(25)
 #define  SDHCI_INT_DATA_END_BIT			BIT(22)
 #define  SDHCI_INT_DATA_CRC			BIT(21)
 #define  SDHCI_INT_DATA_TIMEOUT			BIT(20)
-- 
2.43.0




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v1 3/7] mci: sdhci: honor BROKEN_ADMA_ZEROLEN_DESC / NO_ENDATTR_IN_NOPDESC quirks
  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 ` Johannes Schneider
  2026-06-27 19:43 ` [PATCH v1 4/7] mci: imx-esdhc: mark the uSDHC ADMA2 descriptor quirks Johannes Schneider
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Johannes Schneider @ 2026-06-27 19:43 UTC (permalink / raw)
  To: barebox; +Cc: Johannes Schneider

The ADMA2 table builder always emitted a 64 KiB chunk as a length-0 descriptor
(the 16-bit length field encodes 65536 as 0) and terminated the table with a
separate NOP-END descriptor. Some controllers mishandle these forms; Linux
gates the workarounds behind SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (cap the
length below 64 KiB so it is never 0) and SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
(mark END on the last transfer descriptor instead of a trailing NOP-END).

Add both quirks to the shared SDHCI layer and honor them in the table builder.
Controllers that set neither keep the previous behaviour exactly, so this is a
no-op for everyone until a host driver opts in.

Assisted-by: Claude Opus 4.8 (1M context)
Assisted-by: GitHub Copilot CLI (gpt-5.5)
Signed-off-by: Johannes Schneider <johannes.schneider@leica-geosystems.com>
---
 drivers/mci/sdhci.c | 35 ++++++++++++++++++++++++-----------
 drivers/mci/sdhci.h |  4 ++++
 2 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index ccd5d4b83c..8ed8a59342 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -646,31 +646,44 @@ static int sdhci_adma_write_desc(struct sdhci *host, void **desc,
 static int sdhci_adma_build_table(struct sdhci *host, dma_addr_t addr,
 				  unsigned int len)
 {
+	unsigned int max_len = SDHCI_ADMA2_MAX_LEN;
 	void *desc = host->adma_table;
 	int ret;
 
+	/*
+	 * Some controllers (e.g. the i.MX uSDHC) cannot handle a length-0
+	 * descriptor, which the 16-bit length field uses to encode the full
+	 * SDHCI_ADMA2_MAX_LEN. Cap the chunk just below that so the length is
+	 * never 0.
+	 */
+	if (host->quirks & SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC)
+		max_len = SDHCI_ADMA2_MAX_LEN - 4;
+
 	while (len) {
-		unsigned int chunk = min_t(unsigned int, len,
-					   SDHCI_ADMA2_MAX_LEN);
+		unsigned int chunk = min_t(unsigned int, len, max_len);
+		unsigned int attr = ADMA2_TRAN_VALID;
 
+		len -= chunk;
 		/*
-		 * The length field is 16-bit; a length of 0 encodes
-		 * SDHCI_ADMA2_MAX_LEN bytes per the SD Host Controller
-		 * specification.
+		 * Controllers that ignore the END attribute in a trailing NOP
+		 * descriptor want END on the last transfer descriptor instead
+		 * (SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC).
 		 */
-		ret = sdhci_adma_write_desc(host, &desc, addr, chunk & 0xffff,
-					    ADMA2_TRAN_VALID);
+		if (!len && (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC))
+			attr |= ADMA2_END;
+
+		ret = sdhci_adma_write_desc(host, &desc, addr, chunk & 0xffff, attr);
 		if (ret)
 			return ret;
 
 		addr += chunk;
-		len -= chunk;
 	}
 
-	/* Append a terminating descriptor (nop, end, valid). */
-	ret = sdhci_adma_write_desc(host, &desc, 0, 0, ADMA2_NOP_END_VALID);
+	if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC)
+		return 0;
 
-	return ret;
+	/* Append a terminating descriptor (nop, end, valid). */
+	return sdhci_adma_write_desc(host, &desc, 0, 0, ADMA2_NOP_END_VALID);
 }
 
 void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 558469e8ab..cb3b6d2b17 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -315,6 +315,10 @@ struct sdhci {
 	bool v4_mode;		/* Host Version 4 Enable */
 
 	unsigned int quirks;
+/* Controller ignores the END attribute in a NOP ADMA2 descriptor */
+#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC	BIT(6)
+/* Controller cannot handle a length-0 (== 64 KiB) ADMA2 descriptor */
+#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC	BIT(11)
 #define SDHCI_QUIRK_MISSING_CAPS		BIT(27)
 	unsigned int quirks2;
 /* The system physically doesn't support 1.8v, even if the host does */
-- 
2.43.0




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v1 4/7] mci: imx-esdhc: mark the uSDHC ADMA2 descriptor quirks
  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 ` Johannes Schneider
  2026-06-27 19:43 ` [PATCH v1 5/7] mci: imx-esdhc: support HS400 and HS400ES on i.MX8M Johannes Schneider
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Johannes Schneider @ 2026-06-27 19:43 UTC (permalink / raw)
  To: barebox; +Cc: Johannes Schneider

The i.MX uSDHC ADMA2 engine cannot handle a length-0 (64 KiB) descriptor and
ignores the END attribute in a trailing NOP descriptor; large ADMA transfers
hang otherwise. Set SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC and
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC so the shared table builder emits a safe
descriptor shape, matching Linux sdhci-esdhc-imx. esdhc_populate_sdhci() runs
for both the PBL and the proper driver, so this covers every uSDHC ADMA path.

Assisted-by: Claude Opus 4.8 (1M context)
Signed-off-by: Johannes Schneider <johannes.schneider@leica-geosystems.com>
---
 drivers/mci/imx-esdhc-common.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index eb6a71915f..d31a2ad779 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -285,6 +285,14 @@ void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
 		host->sdhci.read16 = esdhc_op_read16_le_tuning;
 		host->sdhci.write16 = esdhc_op_write16_le_tuning;
 	}
+
+	/*
+	 * The uSDHC ADMA2 engine mishandles a length-0 (64 KiB) descriptor and
+	 * the END attribute in a trailing NOP descriptor.
+	 */
+	if (esdhc_is_usdhc(host))
+		host->sdhci.quirks |= SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
+				      SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
 }
 
 static bool esdhc_use_pio_mode(void)
-- 
2.43.0




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v1 5/7] mci: imx-esdhc: support HS400 and HS400ES on i.MX8M
  2026-06-27 19:43 [PATCH v1 1/7] dma: make dma_mapping_error() NULL-safe Johannes Schneider
                   ` (2 preceding siblings ...)
  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
  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
  5 siblings, 0 replies; 7+ messages in thread
From: Johannes Schneider @ 2026-06-27 19:43 UTC (permalink / raw)
  To: barebox; +Cc: Johannes Schneider

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




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v1 6/7] mci: imx-esdhc: make the transfer mode selectable (PIO/SDMA/ADMA2)
  2026-06-27 19:43 [PATCH v1 1/7] dma: make dma_mapping_error() NULL-safe Johannes Schneider
                   ` (3 preceding siblings ...)
  2026-06-27 19:43 ` [PATCH v1 5/7] mci: imx-esdhc: support HS400 and HS400ES on i.MX8M Johannes Schneider
@ 2026-06-27 19:43 ` 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
  5 siblings, 0 replies; 7+ messages in thread
From: Johannes Schneider @ 2026-06-27 19:43 UTC (permalink / raw)
  To: barebox; +Cc: Johannes Schneider

The driver only offered a PIO-or-DMA toggle (CONFIG_MCI_IMX_ESDHC_PIO) and
never called sdhci_setup_adma(), so the controller-resident ADMA2 engine was
unreachable and DMA always meant SDMA.

Replace the single bool with a choice of PIO/SDMA/ADMA2, defaulting to SDMA so
existing configs keep their current behaviour, and call sdhci_setup_adma() in
probe under the ADMA2 option (mirroring the Rockchip dwcmshc driver). It
returns -ENOTSUPP and cleanly falls back to SDMA on controllers without ADMA2
capability, so the new option is safe on all i.MX/Layerscape variants.

Assisted-by: Claude Opus 4.8 (1M context)
Signed-off-by: Johannes Schneider <johannes.schneider@leica-geosystems.com>
---
 drivers/mci/Kconfig     | 19 ++++++++++++++++---
 drivers/mci/imx-esdhc.c |  7 +++++++
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index b38f7a3bdf..cd8862722c 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -169,11 +169,24 @@ config MCI_IMX_ESDHC
 	  Enable this entry to add support to read and write SD cards on a
 	  Freescale i.MX25/35/51 based system.
 
-config MCI_IMX_ESDHC_PIO
-	bool "use PIO mode"
+choice
+	prompt "i.MX esdhc transfer mode"
+	default MCI_IMX_ESDHC_SDMA
 	depends on MCI_IMX_ESDHC
 	help
-	  mostly useful for debugging. Normally you should use DMA.
+	  How the driver moves data words to/from the controller. SDMA and
+	  ADMA2 use the controller's DMA engine; PIO is CPU-driven and slow.
+
+config MCI_IMX_ESDHC_PIO
+	bool "PIO"
+
+config MCI_IMX_ESDHC_SDMA
+	bool "SDMA"
+
+config MCI_IMX_ESDHC_ADMA2
+	bool "ADMA2"
+
+endchoice
 
 config MCI_OMAP_HSMMC
 	bool "OMAP HSMMC"
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index e123bd3ede..3536ebd6b8 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -698,6 +698,13 @@ static int fsl_esdhc_probe(struct device *dev)
 	if (ret)
 		goto err_clk_disable;
 
+	if (IS_ENABLED(CONFIG_MCI_IMX_ESDHC_ADMA2)) {
+		ret = sdhci_setup_adma(&host->sdhci);
+		if (ret && ret != -ENOTSUPP)
+			dev_warn(dev, "ADMA setup failed (%pe), falling back to SDMA\n",
+				 ERR_PTR(ret));
+	}
+
 	if (esdhc_is_usdhc(host) || esdhc_is_layerscape(host))
 		mci->host_caps |= MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR;
 
-- 
2.43.0




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v1 7/7] mci: imx-esdhc: support DMA in the i.MX8M PBL eMMC loader
  2026-06-27 19:43 [PATCH v1 1/7] dma: make dma_mapping_error() NULL-safe Johannes Schneider
                   ` (4 preceding siblings ...)
  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 ` Johannes Schneider
  5 siblings, 0 replies; 7+ messages in thread
From: Johannes Schneider @ 2026-06-27 19:43 UTC (permalink / raw)
  To: barebox; +Cc: Johannes Schneider

The PBL eMMC loader was hardwired to PIO via esdhc_use_pio_mode(): IN_PBL
forced PIO unconditionally. PIO is slow -- on an i.MX8MM board loading the
~1.6 MB next stage took ~559 ms; SDMA/ADMA2 do it in ~21 ms.

Let esdhc_use_pio_mode() in the PBL honour the SDHCI DMA flags, add a
transfer-mode choice for the loader (PIO default / SDMA / ADMA2), and route
both the imx8m and imx8mp/imx8mn loaders through shared helpers. The PBL has
no mci and runs before BL31, so host.sdhci.version is set by hand (no
__sdhci_read_caps()) and the ADMA2 descriptor table lives in a static buffer
(the PBL has no allocator), compiled in only for the ADMA2 option.

On the imx8mp loader the DMA mode is set up *before* the bootpart EXT_CSD
read so that read also goes through ADMA: a PIO transfer preceding the first
ADMA transfer wedges the uSDHC ADMA engine. Either way the loader retries in
PIO if the DMA attempt fails, so a misconfigured board still boots.

SDMA and ADMA2 perform identically here (the transfer is bus-bound), and SDMA
needs no descriptor table, so it is the simpler default for boards that just
want the speedup.

Assisted-by: Claude Opus 4.8 (1M context)
Signed-off-by: Johannes Schneider <johannes.schneider@leica-geosystems.com>
---
 drivers/mci/Kconfig            | 21 +++++++++++
 drivers/mci/imx-esdhc-common.c | 16 ++++++--
 drivers/mci/imx-esdhc-pbl.c    | 68 +++++++++++++++++++++++++++++++---
 3 files changed, 96 insertions(+), 9 deletions(-)

diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index cd8862722c..e071bb5650 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -288,6 +288,27 @@ config MCI_IMX_ESDHC_PBL
 	bool
 	select MCI_SDHCI
 
+choice
+	prompt "i.MX8M PBL eMMC loader transfer mode"
+	default MCI_IMX_ESDHC_PBL_PIO
+	depends on MCI_IMX_ESDHC_PBL
+	help
+	  How the i.MX8M PBL reads the next boot stage from eMMC. DMA is much
+	  faster than PIO; on failure the loader falls back to PIO so the board
+	  still boots. ADMA2 additionally needs the board PBL to set up an early
+	  malloc pool (pbl_malloc_init) for the descriptor table; SDMA does not.
+
+config MCI_IMX_ESDHC_PBL_PIO
+	bool "PIO"
+
+config MCI_IMX_ESDHC_PBL_SDMA
+	bool "SDMA"
+
+config MCI_IMX_ESDHC_PBL_ADMA2
+	bool "ADMA2"
+
+endchoice
+
 config MCI_ATMEL_PBL
 	bool
 	select MCI_ATMEL
diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index a0c2dee32d..1bf70d4e65 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -295,9 +295,17 @@ void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
 				      SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
 }
 
-static bool esdhc_use_pio_mode(void)
+static bool esdhc_use_pio_mode(struct fsl_esdhc_host *host)
 {
-	return IN_PBL || IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO);
+	/*
+	 * In the PBL default to PIO, unless a caller explicitly opted into
+	 * ADMA2 (SDHCI_USE_ADMA, descriptor table set up) or SDMA
+	 * (SDHCI_USE_SDMA).
+	 */
+	if (IN_PBL)
+		return !(host->sdhci.flags & (SDHCI_USE_ADMA | SDHCI_USE_SDMA));
+
+	return IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO);
 }
 
 static int esdhc_setup_data(struct fsl_esdhc_host *host, struct mci_data *data,
@@ -317,7 +325,7 @@ static int esdhc_setup_data(struct fsl_esdhc_host *host, struct mci_data *data,
 
 	host->sdhci.sdma_boundary = 0;
 
-	if (esdhc_use_pio_mode())
+	if (esdhc_use_pio_mode(host))
 		sdhci_setup_data_pio(&host->sdhci, data);
 	else
 		sdhci_setup_data_dma(&host->sdhci, data, dma);
@@ -411,7 +419,7 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 
 	/* Wait until all of the blocks are transferred */
 	if (data) {
-		if (esdhc_use_pio_mode())
+		if (esdhc_use_pio_mode(host))
 			ret = sdhci_transfer_data_pio(&host->sdhci, cmd, data);
 		else
 			ret = sdhci_transfer_data_dma(&host->sdhci, cmd, data, dma);
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
index fe06d418b0..80e80b24a0 100644
--- a/drivers/mci/imx-esdhc-pbl.c
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -33,6 +33,12 @@
 
 static u8 ext_csd[512] __aligned(64);
 
+#ifdef CONFIG_MCI_IMX_ESDHC_PBL_ADMA2
+/* ADMA2 descriptor table for the PBL eMMC loader (the PBL has no allocator). */
+static u8 esdhc_adma_table[SDHCI_DEFAULT_ADMA_DESCS * SDHCI_ADMA2_32_DESC_SZ]
+	__aligned(8);
+#endif
+
 static int esdhc_send_ext_csd(struct fsl_esdhc_host *host)
 {
 	struct mci_cmd cmd = {};
@@ -252,6 +258,51 @@ int imx7_esdhc_start_image(int instance)
  * Return: If image successfully loaded, returns 0.
  * A negative error code is returned when this function fails.
  */
+/*
+ * Configure the PBL eMMC loader's DMA mode (ADMA2 or SDMA per Kconfig). The
+ * PBL has no mci, so host->sdhci.version is set by hand because
+ * __sdhci_read_caps() cannot run.
+ */
+static void imx8m_esdhc_pbl_setup_dma(struct fsl_esdhc_host *host)
+{
+	if (IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PBL_SDMA)) {
+		host->sdhci.version = SDHCI_SPEC_300;
+		host->sdhci.flags |= SDHCI_USE_SDMA;
+	}
+
+#ifdef CONFIG_MCI_IMX_ESDHC_PBL_ADMA2
+	host->sdhci.version = SDHCI_SPEC_300;
+	host->sdhci.adma_table = esdhc_adma_table;
+	host->sdhci.adma_addr = virt_to_phys(esdhc_adma_table);
+	host->sdhci.desc_sz = SDHCI_ADMA2_32_DESC_SZ;
+	host->sdhci.adma_table_cnt = SDHCI_DEFAULT_ADMA_DESCS;
+	host->sdhci.adma_table_sz = sizeof(esdhc_adma_table);
+	host->sdhci.flags |= SDHCI_USE_ADMA;
+#endif
+}
+
+static int imx8m_esdhc_load_image_dma(struct fsl_esdhc_host *host,
+				      struct esdhc_soc_data *data, int instance,
+				      void *bl33, u32 offset, u32 ivt_offset)
+{
+	int ret;
+
+	ret = esdhc_load_image(host, MX8M_DDR_CSD1_BASE_ADDR,
+			       (ptrdiff_t)bl33, offset, ivt_offset, false);
+	if (!ret)
+		return 0;
+
+	/* Fall back to PIO so a failed DMA attempt still boots. */
+	if (host->sdhci.flags & (SDHCI_USE_ADMA | SDHCI_USE_SDMA)) {
+		host->sdhci.flags &= ~(SDHCI_USE_ADMA | SDHCI_USE_SDMA);
+		imx8m_esdhc_init(host, data, instance);
+		ret = esdhc_load_image(host, MX8M_DDR_CSD1_BASE_ADDR,
+				       (ptrdiff_t)bl33, offset, ivt_offset, false);
+	}
+
+	return ret;
+}
+
 int imx8m_esdhc_load_image(int instance, void *bl33)
 {
 	struct esdhc_soc_data data;
@@ -262,9 +313,10 @@ int imx8m_esdhc_load_image(int instance, void *bl33)
 	if (ret)
 		return ret;
 
-	return esdhc_load_image(&host, MX8M_DDR_CSD1_BASE_ADDR,
-				(ptrdiff_t)bl33, SZ_32K, SZ_1K,
-				false);
+	imx8m_esdhc_pbl_setup_dma(&host);
+
+	return imx8m_esdhc_load_image_dma(&host, &data, instance, bl33,
+					  SZ_32K, SZ_1K);
 }
 
 /**
@@ -291,10 +343,16 @@ int imx8mp_esdhc_load_image(int instance, void *bl33)
 	if (ret)
 		return ret;
 
+	/*
+	 * Set up DMA first so the EXT_CSD read below also goes through ADMA --
+	 * a PIO transfer before the first ADMA transfer wedges the 8MP uSDHC.
+	 */
+	imx8m_esdhc_pbl_setup_dma(&host);
+
 	offset = esdhc_bootpart_active(&host)? 0 : SZ_32K;
 
-	return esdhc_load_image(&host, MX8M_DDR_CSD1_BASE_ADDR,
-				(ptrdiff_t)bl33, offset, 0, false);
+	return imx8m_esdhc_load_image_dma(&host, &data, instance, bl33,
+					  offset, 0);
 }
 
 int imx8mn_esdhc_load_image(int instance, void *bl33)
-- 
2.43.0




^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-06-27 19:45 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH v1 5/7] mci: imx-esdhc: support HS400 and HS400ES on i.MX8M Johannes Schneider
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox