mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 00/30] mci: imx-esdhc: add HS200 support
@ 2025-05-07  8:21 Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 01/30] mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition Ahmad Fatoum
                   ` (30 more replies)
  0 siblings, 31 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox

On my i.MX8MP board this increases sequential read nearly from
72 MiB/s to 131 MiB/s.

I also got HS400ES running, but it gives me occasional CRC errors, so
this series only concerns itself with HS200 for now.

v1 -> v2:
  - use correct speed comparison in commit message. v1 had KASAN
  and DMA_API_DEBUG enabled..
  - define and use new sdhci_compute_timeout helper (Sascha)
  - remove superfluous comment about clearing bits (Sascha)
  - always populate delay settings in boarddata (Sascha)
  - reduce number of Linux SDHCI quirks mentioned in commit
    message to 51 (Sascha)

Ahmad Fatoum (30):
  mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition
  mci: move most recent I/O settings into mci_host::ios
  mci: use struct mci_host::ios inside mci_set_ios
  mci: tuning: fix fallback to DDR52
  mci: sdhci: unmap DMA buffers on timeout
  mci: add MMC_CAP_UHS constants
  mci: rename MMC_CAP_MMC_x_yV_DDR to MMC_CAP_x_yV_DDR as in Linux
  mci: compare host and card caps for supported speeds
  mci: print HS200 capabilities in devinfo
  mci: respect no-1-8-v OF property
  mci: sdhci: add support for struct mci_data::timeout_ns
  mci: imx-esdhc: use unsigned types where appropriate
  mci: imx-esdhc: implement esdhc_poll using sdhci_read32_poll_timeout
  mci: imx-esdhc: drop one extra read of SDHCI_INT_STATUS
  mci: sdhci: add cmd parameter to sdhci_transfer_*
  mci: arasan: introduce mmc_op_tuning helper
  mci: imx-esdhc: flesh out register description
  mci: imx-esdhc: add support for delay/tuning properties in DT
  mci: add mci_set_timing helper
  mci: imx-esdhc: add support for setting drive strength
  mci: sdhci: move SDHCI_MAKE_BLKSZ definition to header
  mci: imx-esdhc: select different pinctrl state depending on frequency
  mci: core: retry MMC_CMD_SET_BLOCKLEN up to 4 times
  mci: imx-esdhc: don't reconfigure clock unless required
  mci: sdhci: fix sdhci_transfer_data MMC_SEND_TUNING compatibility
  mci: core: implement mmc_send_tuning
  mci: imx-esdhc: set burst_length_enable
  mci: imx-esdhc: fixup quirks in standard SDHCI registers
  mci: sdhci: support Linux SDHCI_QUIRK2_BROKEN_HS200 flag
  mci: imx-esdhc: implement HS200 support

 drivers/mci/am654-sdhci.c            |  15 +-
 drivers/mci/arasan-sdhci.c           |  26 ++-
 drivers/mci/atmel-sdhci-common.c     |   2 +-
 drivers/mci/atmel_mci.c              |   4 +-
 drivers/mci/dwcmshc-sdhci.c          |   8 +-
 drivers/mci/imx-esdhc-common.c       | 334 +++++++++++++++++++++-----
 drivers/mci/imx-esdhc.c              | 335 +++++++++++++++++++++++++--
 drivers/mci/imx-esdhc.h              |  96 +++++++-
 drivers/mci/mci-bcm2835.c            |   2 +-
 drivers/mci/mci-core.c               | 331 +++++++++++++++++++-------
 drivers/mci/mmci.c                   |  18 +-
 drivers/mci/rockchip-dwcmshc-sdhci.c |  11 +-
 drivers/mci/sdhci.c                  | 151 +++++++++---
 drivers/mci/sdhci.h                  |  49 +++-
 drivers/mci/stm32_sdmmc2.c           |  16 +-
 include/mci.h                        |  47 +++-
 16 files changed, 1187 insertions(+), 258 deletions(-)

-- 
2.39.5




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

* [PATCH v2 01/30] mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 02/30] mci: move most recent I/O settings into mci_host::ios Ahmad Fatoum
                   ` (29 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

BIT(3) in the SDHCI_TRANSFER_MODE register is not
SDHCI_TRNS_AUTO_CMD12, but SDHCI_TRNS_AUTO_CMD23.

Fix this in preparation for later use of SDHCI_TRNS_AUTO_CMD23.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/sdhci.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 2a8456545d70..841e39af05ee 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -28,7 +28,8 @@
 #define SDHCI_TRANSFER_MODE					0x0c
 #define  SDHCI_MULTIPLE_BLOCKS			BIT(5)
 #define  SDHCI_DATA_TO_HOST			BIT(4)
-#define  SDHCI_TRNS_AUTO_CMD12			BIT(3)
+#define  SDHCI_TRNS_AUTO_CMD23			BIT(3)
+#define  SDHCI_TRNS_AUTO_CMD12			BIT(2)
 #define  SDHCI_BLOCK_COUNT_EN			BIT(1)
 #define  SDHCI_DMA_EN				BIT(0)
 #define SDHCI_COMMAND						0x0e
-- 
2.39.5




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

* [PATCH v2 02/30] mci: move most recent I/O settings into mci_host::ios
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 01/30] mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 03/30] mci: use struct mci_host::ios inside mci_set_ios Ahmad Fatoum
                   ` (28 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We already keep around mci_host::{clock,bus_width,timing}, which hold
the last configured I/O settings.

Before duplicating yet another member in struct mci_host, let's do as
Linux does and embed the active struct mmc_ios directly directly into
the MCI object.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/arasan-sdhci.c           | 14 ++++----
 drivers/mci/atmel_mci.c              |  4 +--
 drivers/mci/mci-core.c               | 48 ++++++++++++++--------------
 drivers/mci/mmci.c                   | 18 +++++------
 drivers/mci/rockchip-dwcmshc-sdhci.c |  2 +-
 drivers/mci/sdhci.c                  | 10 +++---
 drivers/mci/stm32_sdmmc2.c           | 12 +++----
 include/mci.h                        | 10 +++---
 8 files changed, 58 insertions(+), 60 deletions(-)

diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index e987ff654862..0ec4cb57ab76 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -155,7 +155,7 @@ static int arasan_zynqmp_execute_tuning(struct mci_host *mci, u32 opcode)
 	int err;
 
 	/* ZynqMP SD controller does not perform auto tuning in DDR50 mode */
-	if (mci->timing == MMC_TIMING_UHS_DDR50)
+	if (mci->ios.timing == MMC_TIMING_UHS_DDR50)
 		return 0;
 
 	arasan_zynqmp_dll_reset(host, device_id);
@@ -205,9 +205,9 @@ static void arasan_sdhci_set_clock(struct mci_host *mci, unsigned int clock)
 	}
 
 	clk_set_phase(clk_data->sampleclk,
-		      clk_data->clk_phase_in[mci->mci->host->timing]);
+		      clk_data->clk_phase_in[mci->mci->host->ios.timing]);
 	clk_set_phase(clk_data->sdcardclk,
-		      clk_data->clk_phase_out[mci->mci->host->timing]);
+		      clk_data->clk_phase_out[mci->mci->host->ios.timing]);
 
 	sdhci_set_clock(&host->sdhci, clock, mci->f_max);
 }
@@ -312,9 +312,9 @@ static void sdhci_arasan_set_clk_delays(struct sdhci *host)
 	struct sdhci_arasan_clk_data *clk_data = &arasan_sdhci->clk_data;
 
 	clk_set_phase(clk_data->sampleclk,
-		      clk_data->clk_phase_in[mci->timing]);
+		      clk_data->clk_phase_in[mci->ios.timing]);
 	clk_set_phase(clk_data->sdcardclk,
-		      clk_data->clk_phase_out[mci->timing]);
+		      clk_data->clk_phase_out[mci->ios.timing]);
 }
 
 static void arasan_dt_read_clk_phase(struct device *dev,
@@ -372,7 +372,7 @@ static int arasan_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
 	/* Assert DLL Reset */
 	zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_ASSERT);
 
-	switch (host->timing) {
+	switch (host->ios.timing) {
 	case MMC_TIMING_MMC_HS:
 	case MMC_TIMING_SD_HS:
 	case MMC_TIMING_UHS_DDR50:
@@ -441,7 +441,7 @@ static int arasan_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
 	if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
 		return 0;
 
-	switch (host->timing) {
+	switch (host->ios.timing) {
 	case MMC_TIMING_MMC_HS:
 	case MMC_TIMING_SD_HS:
 	case MMC_TIMING_UHS_DDR50:
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
index 18d623f20823..1012feb6c226 100644
--- a/drivers/mci/atmel_mci.c
+++ b/drivers/mci/atmel_mci.c
@@ -35,9 +35,9 @@ static void atmci_info(struct device *mci_dev)
 {
 	struct atmel_mci *host = mci_dev->priv;
 
-	printf("  Bus data width: %d bit\n", host->mci.bus_width);
+	printf("  Bus data width: %d bit\n", host->mci.ios.bus_width);
 
-	printf("  Bus frequency: %u Hz\n", host->mci.clock);
+	printf("  Bus frequency: %u Hz\n", host->mci.ios.clock);
 	printf("  Frequency limits: ");
 	if (host->mci.f_min == 0)
 		printf("no lower limit ");
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index abcda497646d..262535d63006 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -121,7 +121,7 @@ static int mci_set_blocklen(struct mci *mci, unsigned len)
 {
 	struct mci_cmd cmd = {};
 
-	if (mci->host->timing == MMC_TIMING_MMC_DDR52)
+	if (mci->host->ios.timing == MMC_TIMING_MMC_DDR52)
 		return 0;
 
 	mci_setup_cmd(&cmd, MMC_CMD_SET_BLOCKLEN, len, MMC_RSP_R1);
@@ -1032,14 +1032,14 @@ static void mci_set_ios(struct mci *mci)
 {
 	struct mci_host *host = mci->host;
 	struct mci_ios ios = {
-		.bus_width = host->bus_width,
-		.clock = host->clock,
-		.timing = host->timing,
+		.bus_width = host->ios.bus_width,
+		.clock = host->ios.clock,
+		.timing = host->ios.timing,
 	};
 
 	host->ops.set_ios(host, &ios);
 
-	host->actual_clock = host->clock;
+	host->actual_clock = host->ios.clock;
 }
 
 /**
@@ -1058,7 +1058,7 @@ static void mci_set_clock(struct mci *mci, unsigned clock)
 	if (clock < host->f_min)
 		clock = host->f_min;
 
-	host->clock = clock;	/* the new target frequency */
+	host->ios.clock = clock;	/* the new target frequency */
 	mci_set_ios(mci);
 }
 
@@ -1071,7 +1071,7 @@ static void mci_set_bus_width(struct mci *mci, enum mci_bus_width width)
 {
 	struct mci_host *host = mci->host;
 
-	host->bus_width = width;	/* the new target bus width */
+	host->ios.bus_width = width;	/* the new target bus width */
 	mci_set_ios(mci);
 }
 
@@ -1432,7 +1432,7 @@ static int mci_startup_sd(struct mci *mci)
 	}
 
 	if (mci->tran_speed > 25000000)
-		mci->host->timing = MMC_TIMING_SD_HS;
+		mci->host->ios.timing = MMC_TIMING_SD_HS;
 
 	mci_set_clock(mci, mci->tran_speed);
 
@@ -1471,7 +1471,7 @@ static int mci_mmc_try_bus_width(struct mci *mci, enum mci_bus_width bus_width,
 	if (err < 0)
 		goto out;
 
-	mci->host->timing = timing;
+	mci->host->ios.timing = timing;
 	mci_set_bus_width(mci, bus_width);
 
 	switch (bus_width) {
@@ -1526,7 +1526,7 @@ static int mci_mmc_select_bus_width(struct mci *mci)
 		 * 4bit transfer mode. On success set the corresponding
 		 * bus width on the host.
 		 */
-		ret = mci_mmc_try_bus_width(mci, bus_widths[idx], host->timing);
+		ret = mci_mmc_try_bus_width(mci, bus_widths[idx], host->ios.timing);
 		if (ret > 0)
 			break;
 	}
@@ -1548,9 +1548,9 @@ static int mci_mmc_select_hs_ddr(struct mci *mci)
 	if (!(mci_caps(mci) & (MMC_CAP_MMC_1_8V_DDR | MMC_CAP_MMC_3_3V_DDR)))
 		return 0;
 
-	ret = mci_mmc_try_bus_width(mci, host->bus_width, MMC_TIMING_MMC_DDR52);
+	ret = mci_mmc_try_bus_width(mci, host->ios.bus_width, MMC_TIMING_MMC_DDR52);
 	if (ret < 0)
-		return mci_mmc_try_bus_width(mci, host->bus_width, MMC_TIMING_MMC_HS);
+		return mci_mmc_try_bus_width(mci, host->ios.bus_width, MMC_TIMING_MMC_HS);
 
 	/* Block length is fixed to 512 bytes while in DDR mode */
 	mci->read_bl_len = SECTOR_SIZE;
@@ -1666,10 +1666,10 @@ static int mmc_select_hs200(struct mci *mci)
 		 * NB: We can't move to full (HS200) speeds until after we've
 		 * successfully switched over.
 		 */
-		old_timing = mci->host->timing;
-		old_clock = mci->host->clock;
+		old_timing = mci->host->ios.timing;
+		old_clock = mci->host->ios.clock;
 
-		mci->host->timing = MMC_TIMING_MMC_HS200;
+		mci->host->ios.timing = MMC_TIMING_MMC_HS200;
 		mci_set_ios(mci);
 		mci_set_clock(mci, mci->host->hs_max_dtr);
 
@@ -1680,8 +1680,8 @@ static int mmc_select_hs200(struct mci *mci)
 		 * it is a switch error.
 		 */
 		if (err == -EBADMSG) {
-			mci->host->clock = old_clock;
-			mci->host->timing = old_timing;
+			mci->host->ios.clock = old_clock;
+			mci->host->ios.timing = old_timing;
 			mci_set_ios(mci);
 		}
 	}
@@ -1759,7 +1759,7 @@ static int mci_startup_mmc(struct mci *mci)
 		else
 			mci->tran_speed = 26000000;
 
-		host->timing = MMC_TIMING_MMC_HS;
+		host->ios.timing = MMC_TIMING_MMC_HS;
 	}
 
 	if (IS_ENABLED(CONFIG_MCI_TUNING)) {
@@ -1774,7 +1774,7 @@ static int mci_startup_mmc(struct mci *mci)
 			ret = mmc_hs200_tuning(mci);
 
 		if (ret) {
-			host->timing = MMC_TIMING_MMC_HS;
+			host->ios.timing = MMC_TIMING_MMC_HS;
 			mci_switch(mci, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
 		}
 	}
@@ -2448,17 +2448,17 @@ static void mci_info(struct device *dev)
 	}
 
 	printf("Host information:\n");
-	printf("  current clock: %d\n", host->clock);
+	printf("  current clock: %d\n", host->ios.clock);
 
-	if (host->bus_width == MMC_BUS_WIDTH_8)
+	if (host->ios.bus_width == MMC_BUS_WIDTH_8)
 		bw = 8;
-	else if (host->bus_width == MMC_BUS_WIDTH_4)
+	else if (host->ios.bus_width == MMC_BUS_WIDTH_4)
 		bw = 4;
 	else
 		bw = 1;
 
 	printf("  current buswidth: %d\n", bw);
-	printf("  current timing: %s\n", mci_timing_tostr(host->timing));
+	printf("  current timing: %s\n", mci_timing_tostr(host->ios.timing));
 	mci_print_caps(host->host_caps);
 
 	printf("Card information:\n");
@@ -2845,7 +2845,7 @@ static int mci_card_probe(struct mci *mci)
 
 on_error:
 	if (rc != 0) {
-		host->clock = 0;	/* disable the MCI clock */
+		host->ios.clock = 0;	/* disable the MCI clock */
 		mci_set_ios(mci);
 		regulator_disable(host->supply);
 		mci->nr_parts = 0;
diff --git a/drivers/mci/mmci.c b/drivers/mci/mmci.c
index c811d3980f98..1a70776bb125 100644
--- a/drivers/mci/mmci.c
+++ b/drivers/mci/mmci.c
@@ -472,35 +472,35 @@ static void mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
 	sdi_clkcr = mmci_readl(host, MMCICLOCK);
 
 	/* Ramp up the clock rate */
-	if (mci->clock) {
+	if (mci->ios.clock) {
 		u32 clkdiv = 0;
 		u32 tmp_clock;
 
 		dev_dbg(host->hw_dev, "setting clock and bus width in the host:");
-		if (mci->clock >= mci->f_max) {
+		if (mci->ios.clock >= mci->f_max) {
 			clkdiv = 0;
-			mci->clock = mci->f_max;
+			mci->ios.clock = mci->f_max;
 		} else {
-			clkdiv = (host->mclk / mci->clock) - 2;
+			clkdiv = (host->mclk / mci->ios.clock) - 2;
 		}
 		tmp_clock = host->mclk / (clkdiv + 2);
-		while (tmp_clock > mci->clock) {
+		while (tmp_clock > mci->ios.clock) {
 			clkdiv++;
 			tmp_clock = host->mclk / (clkdiv + 2);
 		}
 		if (clkdiv > MCI_CLK_CLKDIV_MASK)
 			clkdiv = MCI_CLK_CLKDIV_MASK;
 		tmp_clock = host->mclk / (clkdiv + 2);
-		mci->clock = tmp_clock;
+		mci->ios.clock = tmp_clock;
 		sdi_clkcr &= ~(MCI_CLK_CLKDIV_MASK);
 		sdi_clkcr |= clkdiv;
 	}
 
 	/* Set the bus width */
-	if (mci->bus_width) {
+	if (mci->ios.bus_width) {
 		u32 buswidth = 0;
 
-		switch (mci->bus_width) {
+		switch (mci->ios.bus_width) {
 		case MMC_BUS_WIDTH_1:
 			buswidth |= MCI_1BIT_BUS;
 			break;
@@ -511,7 +511,7 @@ static void mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
 			buswidth |= MCI_ST_8BIT_BUS;
 			break;
 		default:
-			dev_err(host->hw_dev, "Invalid bus width (%d)\n", mci->bus_width);
+			dev_err(host->hw_dev, "Invalid bus width (%d)\n", mci->ios.bus_width);
 			break;
 		}
 		sdi_clkcr &= ~(MCI_xBIT_BUS_MASK);
diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c
index ff4ba3125873..6f3795465765 100644
--- a/drivers/mci/rockchip-dwcmshc-sdhci.c
+++ b/drivers/mci/rockchip-dwcmshc-sdhci.c
@@ -130,7 +130,7 @@ static void rk_sdhci_set_clock(struct rk_sdhci_host *host, unsigned int clock)
 	u32 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, extra;
 	int err;
 
-	host->mci.clock = 0;
+	host->mci.ios.clock = 0;
 
 	/* DO NOT TOUCH THIS SETTING */
 	extra = DWCMSHC_EMMC_DLL_DLYENA |
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 2c12f00ab967..f54cc6d58b2c 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -87,7 +87,7 @@ static int sdhci_send_tuning(struct sdhci *host, u32 opcode)
 	 * to 64 here.
 	 */
 	if (cmd.cmdidx == MMC_SEND_TUNING_BLOCK_HS200 &&
-	    host->mci->bus_width == MMC_BUS_WIDTH_8) {
+	    host->mci->ios.bus_width == MMC_BUS_WIDTH_8) {
 		sdhci_write16(host, SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 128));
 	} else {
 		sdhci_write16(host, SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 64));
@@ -196,7 +196,7 @@ int sdhci_execute_tuning(struct sdhci *sdhci, u32 opcode)
 	 * If the Host Controller supports the HS200 mode then the
 	 * tuning function has to be executed.
 	 */
-	switch (host->timing) {
+	switch (host->ios.timing) {
 	/* HS400 tuning is done in HS200 mode */
 	case MMC_TIMING_MMC_HS400:
 		err = -EINVAL;
@@ -859,9 +859,9 @@ void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_
 
 	BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
 
-	host->mci->clock = 0;
+	host->mci->ios.clock = 0;
 
-	sdhci_set_uhs_signaling(host, host->mci->timing);
+	sdhci_set_uhs_signaling(host, host->mci->ios.timing);
 
 	sdhci_wait_idle_data(host, NULL);
 
@@ -870,7 +870,7 @@ void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_
 	if (clock == 0)
 		return;
 
-	clk = sdhci_calc_clk(host, clock, &host->mci->clock, input_clock);
+	clk = sdhci_calc_clk(host, clock, &host->mci->ios.clock, input_clock);
 	sdhci_enable_clk(host, clk);
 }
 
diff --git a/drivers/mci/stm32_sdmmc2.c b/drivers/mci/stm32_sdmmc2.c
index 30745ea7c6c0..b517ab7964e2 100644
--- a/drivers/mci/stm32_sdmmc2.c
+++ b/drivers/mci/stm32_sdmmc2.c
@@ -254,7 +254,7 @@ static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv)
 	       priv->base + SDMMC_POWER);
 
 	/* during the first 74 SDMMC_CK cycles the SDMMC is still disabled. */
-	udelay(DIV_ROUND_UP(74 * USEC_PER_SEC, priv->mci.clock));
+	udelay(DIV_ROUND_UP(74 * USEC_PER_SEC, priv->mci.ios.clock));
 }
 
 static dma_addr_t stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
@@ -539,17 +539,17 @@ static int stm32_sdmmc2_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
 {
 	struct stm32_sdmmc2_priv *priv = to_mci_host(mci);
-	u32 desired = mci->clock;
+	u32 desired = mci->ios.clock;
 	u32 sys_clock = clk_get_rate(priv->clk);
 	u32 clk = 0, ddr = 0;
 
 	dev_dbg(priv->dev, "%s: bus_width = %d, clock = %d\n", __func__,
-		mci->bus_width, mci->clock);
+		mci->ios.bus_width, mci->ios.clock);
 
 	if (mci_timing_is_ddr(ios->timing))
 		ddr = SDMMC_CLKCR_DDR;
 
-	if (mci->clock)
+	if (mci->ios.clock)
 		stm32_sdmmc2_pwron(priv);
 	else
 		stm32_sdmmc2_pwrcycle(priv);
@@ -570,9 +570,9 @@ static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
 
 	clk |= ddr;
 
-	if (mci->bus_width == MMC_BUS_WIDTH_4)
+	if (mci->ios.bus_width == MMC_BUS_WIDTH_4)
 		clk |= SDMMC_CLKCR_WIDBUS_4;
-	if (mci->bus_width == MMC_BUS_WIDTH_8)
+	if (mci->ios.bus_width == MMC_BUS_WIDTH_8)
 		clk |= SDMMC_CLKCR_WIDBUS_8;
 
 	writel(clk | priv->clk_reg_msk | SDMMC_CLKCR_HWFC_EN,
diff --git a/include/mci.h b/include/mci.h
index 10bba878a8f9..7ee2e4faa90c 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -606,10 +606,8 @@ struct mci_host {
 #define MMC_CAP2_CRYPTO		0
 	unsigned f_min;		/**< host interface lower limit */
 	unsigned f_max;		/**< host interface upper limit */
-	unsigned clock;		/**< Current clock used to talk to the card */
 	unsigned actual_clock;
-	enum mci_bus_width bus_width;	/**< used data bus width to the card */
-	enum mci_timing timing;	/**< used timing specification to the card */
+	struct mci_ios ios;		/* current io bus settings */
 	unsigned hs_max_dtr;
 	unsigned hs200_max_dtr;
 	unsigned max_req_size;
@@ -739,8 +737,8 @@ static inline struct mci *mci_get_device_by_devpath(const char *devpath)
 
 static inline int mmc_card_hs(struct mci *mci)
 {
-	return mci->host->timing == MMC_TIMING_SD_HS ||
-		mci->host->timing == MMC_TIMING_MMC_HS;
+	return mci->host->ios.timing == MMC_TIMING_SD_HS ||
+		mci->host->ios.timing == MMC_TIMING_MMC_HS;
 }
 
 /*
@@ -760,7 +758,7 @@ int mci_rpmb_route_frames(struct mci *mci, void *req, unsigned long reqlen,
 
 static inline bool mmc_card_hs200(struct mci *mci)
 {
-	return mci->host->timing == MMC_TIMING_MMC_HS200;
+	return mci->host->ios.timing == MMC_TIMING_MMC_HS200;
 }
 
 #endif /* _MCI_H_ */
-- 
2.39.5




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

* [PATCH v2 03/30] mci: use struct mci_host::ios inside mci_set_ios
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 01/30] mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 02/30] mci: move most recent I/O settings into mci_host::ios Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 04/30] mci: tuning: fix fallback to DDR52 Ahmad Fatoum
                   ` (27 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Now that we have a struct mci_ios inside struct mci_host, let's make use
of it directly in mci_set_ios instead of copying out the struct members
one by one.

While at it, let's also add a debug print describing the configuration
being applied.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/mci-core.c | 48 +++++++++++++++++++++---------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 262535d63006..7b33b4c8df88 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1024,6 +1024,24 @@ static int sd_change_freq(struct mci *mci)
 	return 0;
 }
 
+static const char *mci_timing_tostr(unsigned timing)
+{
+	switch (timing) {
+	case MMC_TIMING_LEGACY:
+		return "legacy";
+	case MMC_TIMING_MMC_HS:
+		return "MMC HS";
+	case MMC_TIMING_SD_HS:
+		return "SD HS";
+	case MMC_TIMING_MMC_DDR52:
+		return "MMC DDR52";
+	case MMC_TIMING_MMC_HS200:
+		return "HS200";
+	default:
+		return "unknown"; /* shouldn't happen */
+	}
+}
+
 /**
  * Setup host's interface bus width and transfer frequency
  * @param mci MCI instance
@@ -1031,13 +1049,13 @@ static int sd_change_freq(struct mci *mci)
 static void mci_set_ios(struct mci *mci)
 {
 	struct mci_host *host = mci->host;
-	struct mci_ios ios = {
-		.bus_width = host->ios.bus_width,
-		.clock = host->ios.clock,
-		.timing = host->ios.timing,
-	};
+	struct mci_ios *ios = &host->ios;
 
-	host->ops.set_ios(host, &ios);
+	dev_dbg(&mci->dev, "clock %u.%uMHz width %u timing %s\n",
+		ios->clock / 1000000, ios->clock % 1000000,
+		1 << ios->bus_width, mci_timing_tostr(ios->timing));
+
+	host->ops.set_ios(host, ios);
 
 	host->actual_clock = host->ios.clock;
 }
@@ -2313,24 +2331,6 @@ static int mci_sd_read(struct block_device *blk, void *buffer, sector_t block,
 
 /* ------------------ attach to the device API --------------------------- */
 
-static const char *mci_timing_tostr(unsigned timing)
-{
-	switch (timing) {
-	case MMC_TIMING_LEGACY:
-		return "legacy";
-	case MMC_TIMING_MMC_HS:
-		return "MMC HS";
-	case MMC_TIMING_SD_HS:
-		return "SD HS";
-	case MMC_TIMING_MMC_DDR52:
-		return "MMC DDR52";
-	case MMC_TIMING_MMC_HS200:
-		return "HS200";
-	default:
-		return "unknown"; /* shouldn't happen */
-	}
-}
-
 static void mci_print_caps(unsigned caps)
 {
 	printf("  capabilities: %s%s%s%s%s%s%s%s\n",
-- 
2.39.5




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

* [PATCH v2 04/30] mci: tuning: fix fallback to DDR52
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 03/30] mci: use struct mci_host::ios inside mci_set_ios Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 05/30] mci: sdhci: unmap DMA buffers on timeout Ahmad Fatoum
                   ` (26 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

When CONFIG_MCI_TUNING is enabled, but a card mmc_card_hs200 return
false, the fallback currently puts the card into high speed mode instead
of DDR52, which would've been the next best thing.

Rework the code to fix this.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/mci-core.c | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 7b33b4c8df88..5a3bb9d1ad13 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1781,34 +1781,35 @@ static int mci_startup_mmc(struct mci *mci)
 	}
 
 	if (IS_ENABLED(CONFIG_MCI_TUNING)) {
-		/*
-		 * Select timing interface
-		 */
+		dev_dbg(&mci->dev, "select timing %s\n", mci_timing_tostr(host->ios.timing));
+
 		ret = mmc_select_timing(mci);
 		if (ret)
 			return ret;
 
-		if (mmc_card_hs200(mci))
+		if (mmc_card_hs200(mci)) {
 			ret = mmc_hs200_tuning(mci);
+			if (!ret) {
+				dev_dbg(&mci->dev, "HS200 tuning succeeded\n");
+				return 0;
+			}
+
+			dev_dbg(&mci->dev, "HS200 tuning failed, falling back to HS\n");
 
-		if (ret) {
 			host->ios.timing = MMC_TIMING_MMC_HS;
 			mci_switch(mci, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
 		}
 	}
 
-	if (ret || !IS_ENABLED(CONFIG_MCI_TUNING)) {
-		mci_set_clock(mci, mci->tran_speed);
+	mci_set_clock(mci, mci->tran_speed);
 
-		/* find out maximum bus width and then try DDR if supported */
-		ret = mci_mmc_select_bus_width(mci);
-		if (ret > MMC_BUS_WIDTH_1 && mci->tran_speed == 52000000)
-			ret = mci_mmc_select_hs_ddr(mci);
+	/* find out maximum bus width and then try DDR if supported */
+	ret = mci_mmc_select_bus_width(mci);
+	if (ret > MMC_BUS_WIDTH_1 && mci->tran_speed == 52000000)
+		ret = mci_mmc_select_hs_ddr(mci);
 
-		if (ret < 0) {
-			dev_warn(&mci->dev, "Changing MMC bus width failed: %d\n", ret);
-		}
-	}
+	if (ret < 0)
+		dev_warn(&mci->dev, "Changing MMC bus width failed: %d\n", ret);
 
 	return ret >= MMC_BUS_WIDTH_1 ? 0 : ret;
 }
-- 
2.39.5




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

* [PATCH v2 05/30] mci: sdhci: unmap DMA buffers on timeout
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 04/30] mci: tuning: fix fallback to DDR52 Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 06/30] mci: add MMC_CAP_UHS constants Ahmad Fatoum
                   ` (25 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We map DMA buffers during sdhci_setup_data_dma and unmap them inside
sdhci_transfer_data_dma, but if an error happens between these two
functions, DMA buffers are never unmapped.

Fix this to silence CONFIG_DMA_API_DEBUG observed during a timeout on an
i.MX8M.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/am654-sdhci.c            |  4 +++-
 drivers/mci/arasan-sdhci.c           |  4 +++-
 drivers/mci/dwcmshc-sdhci.c          |  4 +++-
 drivers/mci/imx-esdhc-common.c       | 19 +++++++++++++------
 drivers/mci/rockchip-dwcmshc-sdhci.c |  4 +++-
 drivers/mci/sdhci.c                  | 22 ++++++++++++++++++----
 drivers/mci/sdhci.h                  |  1 +
 7 files changed, 44 insertions(+), 14 deletions(-)

diff --git a/drivers/mci/am654-sdhci.c b/drivers/mci/am654-sdhci.c
index 2c1fa5d80451..a378e3d53c91 100644
--- a/drivers/mci/am654-sdhci.c
+++ b/drivers/mci/am654-sdhci.c
@@ -488,8 +488,10 @@ static int am654_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
 
 	ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
-	if (ret)
+	if (ret) {
+		sdhci_teardown_data(&host->sdhci, data, dma);
 		goto error;
+	}
 
 	sdhci_read_response(&host->sdhci, cmd);
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index 0ec4cb57ab76..f98f7eb3d10b 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -284,8 +284,10 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 		mask = SDHCI_INT_DATA_AVAIL;
 
 	ret = sdhci_wait_for_done(&host->sdhci, mask);
-	if (ret)
+	if (ret) {
+		sdhci_teardown_data(&host->sdhci, data, dma);
 		goto error;
+	}
 
 	sdhci_read_response(&host->sdhci, cmd);
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
diff --git a/drivers/mci/dwcmshc-sdhci.c b/drivers/mci/dwcmshc-sdhci.c
index 8398889a703c..f6d559e393d4 100644
--- a/drivers/mci/dwcmshc-sdhci.c
+++ b/drivers/mci/dwcmshc-sdhci.c
@@ -153,8 +153,10 @@ static int do_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
 
 	ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
-	if (ret)
+	if (ret) {
+		sdhci_teardown_data(&host->sdhci, data, dma);
 		goto error;
+	}
 
 	sdhci_read_response(&host->sdhci, cmd);
 
diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index a51e38734cb3..7673220fe8d4 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -164,16 +164,20 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 			 100 * MSECOND);
 	if (ret) {
 		dev_dbg(host->dev, "timeout 1\n");
-		return -ETIMEDOUT;
+		goto undo_setup_data;
 	}
 
 	irqstat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
 
-	if (irqstat & CMD_ERR)
-		return -EIO;
+	if (irqstat & CMD_ERR) {
+		ret = -EIO;
+		goto undo_setup_data;
+	}
 
-	if (irqstat & SDHCI_INT_TIMEOUT)
-		return -ETIMEDOUT;
+	if (irqstat & SDHCI_INT_TIMEOUT) {
+		ret = -ETIMEDOUT;
+		goto undo_setup_data;
+	}
 
 	/* Workaround for ESDHC errata ENGcm03648 / ENGcm12360 */
 	if (!data && (cmd->resp_type & MMC_RSP_BUSY)) {
@@ -186,7 +190,7 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 				 2500 * MSECOND);
 		if (ret) {
 			dev_err(host->dev, "timeout PRSSTAT_DAT0\n");
-			return -ETIMEDOUT;
+			goto undo_setup_data;
 		}
 	}
 
@@ -223,5 +227,8 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 	}
 
 	return 0;
+undo_setup_data:
+	sdhci_teardown_data(&host->sdhci, data, dma);
+	return ret;
 }
 
diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c
index 6f3795465765..a7da64f4d7db 100644
--- a/drivers/mci/rockchip-dwcmshc-sdhci.c
+++ b/drivers/mci/rockchip-dwcmshc-sdhci.c
@@ -269,8 +269,10 @@ static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
 
 	ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
-	if (ret)
+	if (ret) {
+		sdhci_teardown_data(&host->sdhci, data, dma);
 		goto error;
+	}
 
 	sdhci_read_response(&host->sdhci, cmd);
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index f54cc6d58b2c..d0caee22f2f7 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -507,6 +507,23 @@ void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
 	sdhci_set_sdma_addr(sdhci, *dma);
 }
 
+void sdhci_teardown_data(struct sdhci *sdhci,
+			 struct mci_data *data, dma_addr_t dma)
+{
+	struct device *dev = sdhci_dev(sdhci);
+	unsigned nbytes;
+
+	if (IN_PBL || !data || dma_mapping_error(dev, dma))
+		return;
+
+	nbytes = data->blocks * data->blocksize;
+
+	if (data->flags & MMC_DATA_READ)
+		dma_unmap_single(dev, dma, nbytes, DMA_FROM_DEVICE);
+	else
+		dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE);
+}
+
 int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
 			    dma_addr_t dma)
 {
@@ -572,10 +589,7 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
 
 	ret = 0;
 out:
-	if (data->flags & MMC_DATA_READ)
-		dma_unmap_single(dev, dma, nbytes, DMA_FROM_DEVICE);
-	else
-		dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE);
+	sdhci_teardown_data(sdhci, data, dma);
 
 	return ret;
 }
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 841e39af05ee..d3b681153134 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -323,6 +323,7 @@ void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
 			     u32 *xfer);
 void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data);
 void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data, dma_addr_t *dma);
+void sdhci_teardown_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma);
 int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma);
 int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data);
 int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
-- 
2.39.5




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

* [PATCH v2 06/30] mci: add MMC_CAP_UHS constants
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 05/30] mci: sdhci: unmap DMA buffers on timeout Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 07/30] mci: rename MMC_CAP_MMC_x_yV_DDR to MMC_CAP_x_yV_DDR as in Linux Ahmad Fatoum
                   ` (24 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We currently support higher speed modes only for eMMC, not SD, but to
simplify porting drivers, let's add the capability bits into the header.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/am654-sdhci.c | 6 ------
 include/mci.h             | 8 ++++++++
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/mci/am654-sdhci.c b/drivers/mci/am654-sdhci.c
index a378e3d53c91..20dd03b83a9a 100644
--- a/drivers/mci/am654-sdhci.c
+++ b/drivers/mci/am654-sdhci.c
@@ -83,12 +83,6 @@
 #define AM654_SDHCI_MIN_FREQ	400000
 #define CLOCK_TOO_SLOW_HZ	50000000
 
-#define MMC_CAP_UHS_SDR104 0
-#define MMC_CAP_UHS_SDR12 0
-#define MMC_CAP_UHS_DDR50 0
-#define MMC_CAP_UHS_SDR25 0
-#define MMC_CAP_UHS_SDR50 0
-
 struct timing_data {
 	const char *otap_binding;
 	const char *itap_binding;
diff --git a/include/mci.h b/include/mci.h
index 7ee2e4faa90c..bb168ca2225f 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -58,6 +58,14 @@
 #define MMC_CAP_MMC_1_2V_DDR		(1 << 9)	/* Host supports eMMC DDR 1.2V */
 #define MMC_CAP_DDR			(MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR | \
 					 MMC_CAP_MMC_1_2V_DDR)
+#define MMC_CAP_UHS_SDR12	(1 << 16)	/* Host supports UHS SDR12 mode */
+#define MMC_CAP_UHS_SDR25	(1 << 17)	/* Host supports UHS SDR25 mode */
+#define MMC_CAP_UHS_SDR50	(1 << 18)	/* Host supports UHS SDR50 mode */
+#define MMC_CAP_UHS_SDR104	(1 << 19)	/* Host supports UHS SDR104 mode */
+#define MMC_CAP_UHS_DDR50	(1 << 20)	/* Host supports UHS DDR50 mode */
+#define MMC_CAP_UHS		(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | \
+				 MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | \
+				 MMC_CAP_UHS_DDR50)
 /* Mask of all caps for bus width */
 #define MMC_CAP_BIT_DATA_MASK		(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)
 
-- 
2.39.5




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

* [PATCH v2 07/30] mci: rename MMC_CAP_MMC_x_yV_DDR to MMC_CAP_x_yV_DDR as in Linux
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 06/30] mci: add MMC_CAP_UHS constants Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 08/30] mci: compare host and card caps for supported speeds Ahmad Fatoum
                   ` (23 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Linux names these parameters without listing MMC twice in the macro.
Follow suit in barebox to make porting a tiny bit easier.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc.c    |  2 +-
 drivers/mci/mci-core.c     | 10 +++++-----
 drivers/mci/stm32_sdmmc2.c |  4 ++--
 include/mci.h              | 10 +++++-----
 4 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index ebc7ed539da9..923dae9cf3b0 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -324,7 +324,7 @@ static int fsl_esdhc_probe(struct device *dev)
 		goto err_clk_disable;
 
 	if (esdhc_is_usdhc(host) || esdhc_is_layerscape(host))
-		mci->host_caps |= MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR;
+		mci->host_caps |= MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR;
 
 	rate = clk_get_rate(host->clk);
 	host->mci.f_min = rate >> 12;
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 5a3bb9d1ad13..819a059e6468 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -791,7 +791,7 @@ static int mmc_change_freq(struct mci *mci)
 		mci->card_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
 
 		if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V)
-			mci->card_caps |= MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR;
+			mci->card_caps |= MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR;
 	}
 
 	if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) &&
@@ -1563,7 +1563,7 @@ static int mci_mmc_select_hs_ddr(struct mci *mci)
 	 * higher speed modes that require voltage switching like HS200/HS400,
 	 * let's just check for either bit.
 	 */
-	if (!(mci_caps(mci) & (MMC_CAP_MMC_1_8V_DDR | MMC_CAP_MMC_3_3V_DDR)))
+	if (!(mci_caps(mci) & (MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR)))
 		return 0;
 
 	ret = mci_mmc_try_bus_width(mci, host->ios.bus_width, MMC_TIMING_MMC_DDR52);
@@ -2340,9 +2340,9 @@ static void mci_print_caps(unsigned caps)
 		caps & MMC_CAP_SD_HIGHSPEED ? "sd-hs " : "",
 		caps & MMC_CAP_MMC_HIGHSPEED ? "mmc-hs " : "",
 		caps & MMC_CAP_MMC_HIGHSPEED_52MHZ ? "mmc-52MHz " : "",
-		caps & MMC_CAP_MMC_3_3V_DDR ? "ddr-3.3v " : "",
-		caps & MMC_CAP_MMC_1_8V_DDR ? "ddr-1.8v " : "",
-		caps & MMC_CAP_MMC_1_2V_DDR ? "ddr-1.2v " : "");
+		caps & MMC_CAP_3_3V_DDR ? "ddr-3.3v " : "",
+		caps & MMC_CAP_1_8V_DDR ? "ddr-1.8v " : "",
+		caps & MMC_CAP_1_2V_DDR ? "ddr-1.2v " : "");
 }
 
 /*
diff --git a/drivers/mci/stm32_sdmmc2.c b/drivers/mci/stm32_sdmmc2.c
index b517ab7964e2..8aeda6e988b1 100644
--- a/drivers/mci/stm32_sdmmc2.c
+++ b/drivers/mci/stm32_sdmmc2.c
@@ -637,9 +637,9 @@ static int stm32_sdmmc2_probe(struct amba_device *adev,
 		mci->host_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
 
 	if (of_property_read_bool(np, "mmc-ddr-3_3v"))
-		mci->host_caps |= MMC_CAP_MMC_3_3V_DDR;
+		mci->host_caps |= MMC_CAP_3_3V_DDR;
 	if (of_property_read_bool(np, "mmc-ddr-1_8v"))
-		mci->host_caps |= MMC_CAP_MMC_1_8V_DDR;
+		mci->host_caps |= MMC_CAP_1_8V_DDR;
 
 	return mci_register(&priv->mci);
 
diff --git a/include/mci.h b/include/mci.h
index bb168ca2225f..126d3fe52d37 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -53,11 +53,11 @@
 #define MMC_CAP_SD_HIGHSPEED		(1 << 3)
 #define MMC_CAP_MMC_HIGHSPEED		(1 << 4)
 #define MMC_CAP_MMC_HIGHSPEED_52MHZ	(1 << 5)
-#define MMC_CAP_MMC_3_3V_DDR		(1 << 7)	/* Host supports eMMC DDR 3.3V */
-#define MMC_CAP_MMC_1_8V_DDR		(1 << 8)	/* Host supports eMMC DDR 1.8V */
-#define MMC_CAP_MMC_1_2V_DDR		(1 << 9)	/* Host supports eMMC DDR 1.2V */
-#define MMC_CAP_DDR			(MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR | \
-					 MMC_CAP_MMC_1_2V_DDR)
+#define MMC_CAP_3_3V_DDR		(1 << 7)	/* Host supports eMMC DDR 3.3V */
+#define MMC_CAP_1_8V_DDR		(1 << 8)	/* Host supports eMMC DDR 1.8V */
+#define MMC_CAP_1_2V_DDR		(1 << 9)	/* Host supports eMMC DDR 1.2V */
+#define MMC_CAP_DDR			(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR | \
+					 MMC_CAP_1_2V_DDR)
 #define MMC_CAP_UHS_SDR12	(1 << 16)	/* Host supports UHS SDR12 mode */
 #define MMC_CAP_UHS_SDR25	(1 << 17)	/* Host supports UHS SDR25 mode */
 #define MMC_CAP_UHS_SDR50	(1 << 18)	/* Host supports UHS SDR50 mode */
-- 
2.39.5




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

* [PATCH v2 08/30] mci: compare host and card caps for supported speeds
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (6 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 07/30] mci: rename MMC_CAP_MMC_x_yV_DDR to MMC_CAP_x_yV_DDR as in Linux Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 09/30] mci: print HS200 capabilities in devinfo Ahmad Fatoum
                   ` (22 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We currently support only a single speed mode that needs tuning, so we
took the existence of execute_tuning to mean that higher speed modes are
supported.

This breaks down once we have multiple speed modes that need tuning and
is different to what Linux does, which instead sets capabilities on the
host, which are compared with the capabilities of the card.

Do the same in barebox to get rid of this quirk.

No functional change intended.

Suggested-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/arasan-sdhci.c |  1 +
 drivers/mci/mci-core.c     | 22 ++++++++++------------
 include/mci.h              |  1 +
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index f98f7eb3d10b..879339572b11 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -771,6 +771,7 @@ static int arasan_sdhci_probe(struct device *dev)
 	if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) {
 		if (IS_ENABLED(CONFIG_MCI_TUNING))
 			mci->ops.execute_tuning = arasan_zynqmp_execute_tuning;
+		mci->caps2 |= MMC_CAP2_HS200;
 		arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN;
 	}
 
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 819a059e6468..ed036719beb7 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1582,14 +1582,8 @@ int mci_execute_tuning(struct mci *mci)
 	struct mci_host *host = mci->host;
 	u32 opcode;
 
-	if (!host->ops.execute_tuning) {
-		/*
-		 * For us, implementing ->execute_tuning is mandatory to
-		 * support higher speed modes
-		 */
-		dev_warn(&mci->dev, "tuning failed: no host diver support\n");
-		return -EOPNOTSUPP;
-	}
+	if (!host->ops.execute_tuning)
+		return 0;
 
 	/* Tuning is only supported for MMC / HS200 */
 	if (mmc_card_hs200(mci))
@@ -1626,29 +1620,35 @@ static void mmc_select_max_dtr(struct mci *mci)
 	u32 caps = mci->card_caps;
 	unsigned int hs_max_dtr = 0;
 	unsigned int hs200_max_dtr = 0;
+	unsigned int avail_type = 0;
 
 	if ((caps & MMC_CAP_MMC_HIGHSPEED) &&
 	    (card_type & EXT_CSD_CARD_TYPE_26)) {
 		hs_max_dtr = MMC_HIGH_26_MAX_DTR;
+		avail_type |= EXT_CSD_CARD_TYPE_26;
 	}
 
 	if ((caps & MMC_CAP_MMC_HIGHSPEED) &&
 	    (card_type & EXT_CSD_CARD_TYPE_52)) {
 		hs_max_dtr = MMC_HIGH_52_MAX_DTR;
+		avail_type |= EXT_CSD_CARD_TYPE_52;
 	}
 
 	if ((caps2 & MMC_CAP2_HS200_1_8V_SDR) &&
 	    (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) {
 		hs200_max_dtr = MMC_HS200_MAX_DTR;
+		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V;
 	}
 
 	if ((caps2 & MMC_CAP2_HS200_1_2V_SDR) &&
 	    (card_type & EXT_CSD_CARD_TYPE_HS200_1_2V)) {
 		hs200_max_dtr = MMC_HS200_MAX_DTR;
+		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V;
 	}
 
 	mci->host->hs200_max_dtr = hs200_max_dtr;
 	mci->host->hs_max_dtr = hs_max_dtr;
+	mci->host->mmc_avail_type = avail_type;
 }
 /*
  * For device supporting HS200 mode, the following sequence
@@ -1733,16 +1733,14 @@ static void mmc_set_bus_speed(struct mci *mci)
  */
 int mmc_select_timing(struct mci *mci)
 {
-	unsigned int mmc_avail_type;
 	int err = 0;
 
 	mmc_select_max_dtr(mci);
 
-	mmc_avail_type = mci->ext_csd[EXT_CSD_DEVICE_TYPE] & EXT_CSD_CARD_TYPE_MASK;
-	if (mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) {
+	if (mci->host->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) {
 		err = mmc_select_hs200(mci);
 		if (err == -EBADMSG)
-			mmc_avail_type &= ~EXT_CSD_CARD_TYPE_HS200;
+			mci->host->mmc_avail_type &= ~EXT_CSD_CARD_TYPE_HS200;
 		else
 			goto out;
 	}
diff --git a/include/mci.h b/include/mci.h
index 126d3fe52d37..56527f956802 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -616,6 +616,7 @@ struct mci_host {
 	unsigned f_max;		/**< host interface upper limit */
 	unsigned actual_clock;
 	struct mci_ios ios;		/* current io bus settings */
+	unsigned mmc_avail_type;	/**< supported device type by both host and card */
 	unsigned hs_max_dtr;
 	unsigned hs200_max_dtr;
 	unsigned max_req_size;
-- 
2.39.5




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

* [PATCH v2 09/30] mci: print HS200 capabilities in devinfo
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (7 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 08/30] mci: compare host and card caps for supported speeds Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 10/30] mci: respect no-1-8-v OF property Ahmad Fatoum
                   ` (21 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

HS200 capabilities didn't fit into host_caps (named just caps in Linux)
and instead were added to caps2. We copied the same scheme in barebox,
but only evaluate host_caps. Let's do the same for caps2, so devinfo
output can show whether HS200 is supported at a glance.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/mci-core.c | 34 +++++++++++++++++++++++++++++-----
 1 file changed, 29 insertions(+), 5 deletions(-)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index ed036719beb7..9031308ec3d2 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1650,6 +1650,28 @@ static void mmc_select_max_dtr(struct mci *mci)
 	mci->host->hs_max_dtr = hs_max_dtr;
 	mci->host->mmc_avail_type = avail_type;
 }
+
+static u32 mmc_card_caps2_from_ext_csd(struct mci *mci)
+{
+	u8 card_type;
+	u32 caps2;
+
+	if (!mci->ext_csd)
+		return 0;
+
+	card_type = mci->ext_csd[EXT_CSD_DEVICE_TYPE];
+	caps2 = 0;
+
+	if (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
+		caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+
+	if ((caps2 & MMC_CAP2_HS200_1_2V_SDR) &&
+	    (card_type & EXT_CSD_CARD_TYPE_HS200_1_2V))
+		caps2 |= MMC_CAP2_HS200_1_2V_SDR;
+
+	return caps2;
+}
+
 /*
  * For device supporting HS200 mode, the following sequence
  * should be done before executing the tuning process.
@@ -2330,9 +2352,9 @@ static int mci_sd_read(struct block_device *blk, void *buffer, sector_t block,
 
 /* ------------------ attach to the device API --------------------------- */
 
-static void mci_print_caps(unsigned caps)
+static void mci_print_caps(unsigned caps, unsigned caps2)
 {
-	printf("  capabilities: %s%s%s%s%s%s%s%s\n",
+	printf("  capabilities: %s%s%s%s%s%s%s%s%s%s\n",
 		caps & MMC_CAP_4_BIT_DATA ? "4bit " : "",
 		caps & MMC_CAP_8_BIT_DATA ? "8bit " : "",
 		caps & MMC_CAP_SD_HIGHSPEED ? "sd-hs " : "",
@@ -2340,7 +2362,9 @@ static void mci_print_caps(unsigned caps)
 		caps & MMC_CAP_MMC_HIGHSPEED_52MHZ ? "mmc-52MHz " : "",
 		caps & MMC_CAP_3_3V_DDR ? "ddr-3.3v " : "",
 		caps & MMC_CAP_1_8V_DDR ? "ddr-1.8v " : "",
-		caps & MMC_CAP_1_2V_DDR ? "ddr-1.2v " : "");
+		caps & MMC_CAP_1_2V_DDR ? "ddr-1.2v " : "",
+		caps2 & MMC_CAP2_HS200_1_8V_SDR ? "hs200-1.8v " : "",
+		caps2 & MMC_CAP2_HS200_1_2V_SDR ? "hs200-1.2v " : "");
 }
 
 /*
@@ -2458,7 +2482,7 @@ static void mci_info(struct device *dev)
 
 	printf("  current buswidth: %d\n", bw);
 	printf("  current timing: %s\n", mci_timing_tostr(host->ios.timing));
-	mci_print_caps(host->host_caps);
+	mci_print_caps(host->host_caps, host->caps2);
 
 	printf("Card information:\n");
 	printf("  Card type: %s\n", mci->sdio ? "SDIO" : IS_SD(mci) ? "SD" : "MMC");
@@ -2488,7 +2512,7 @@ static void mci_info(struct device *dev)
 	printf("   CSD: %08X-%08X-%08X-%08X\n", mci->csd[0], mci->csd[1],
 		mci->csd[2], mci->csd[3]);
 	printf("  Max. transfer speed: %u Hz\n", mci->tran_speed);
-	mci_print_caps(mci->card_caps);
+	mci_print_caps(mci->card_caps, mmc_card_caps2_from_ext_csd(mci));
 	printf("  Manufacturer ID: 0x%02x\n", mci->cid.manfid);
 	printf("  OEM/Application ID: 0x%04x\n", mci->cid.oemid);
 	printf("  Product name: '%s'\n", mci->cid.prod_name);
-- 
2.39.5




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

* [PATCH v2 10/30] mci: respect no-1-8-v OF property
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (8 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 09/30] mci: print HS200 capabilities in devinfo Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 11/30] mci: sdhci: add support for struct mci_data::timeout_ns Ahmad Fatoum
                   ` (20 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

no-1-8-v is much more relevant to SD-Cards, but on the off-chance that
is' set on an eMMC node, we should respect it and disable 1.8V speed
modes accordingly.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/mci-core.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 9031308ec3d2..2f80763d9831 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -3097,6 +3097,17 @@ void mci_of_parse_node(struct mci_host *host,
 		if (of_property_read_bool(np, "no-mmc-hs400"))
 			host->caps2 &= ~(MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V |
 					 MMC_CAP2_HS400_ES);
+		if (of_property_read_bool(np, "no-1-8-v")) {
+			/*
+			 * The SDHCI controller in a SoC might support HS200/HS400
+			 * (indicated using mmc-hs200-1_8v/mmc-hs400-1_8v dt property),
+			 * but if the board is modeled such that the IO lines are not
+			 * connected to 1.8v then HS200/HS400 cannot be supported.
+			 * Disable HS200/HS400 if the board does not have 1.8v connected
+			 * to the IO lines. (Applicable for other modes in 1.8v)
+			 */
+			host->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES);
+		}
 	}
 }
 
-- 
2.39.5




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

* [PATCH v2 11/30] mci: sdhci: add support for struct mci_data::timeout_ns
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (9 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 10/30] mci: respect no-1-8-v OF property Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 12/30] mci: imx-esdhc: use unsigned types where appropriate Ahmad Fatoum
                   ` (19 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

With tuning sequences, we will want to associate the busy timeout with
the struct mci_data, so add a timeout_ns member to it like Linux does.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/sdhci.c | 17 +++++++----------
 drivers/mci/sdhci.h | 23 ++++++++++++++++++++++-
 include/mci.h       |  1 +
 3 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index d0caee22f2f7..84acb3d163b5 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -813,11 +813,10 @@ void sdhci_enable_clk(struct sdhci *host, u16 clk)
 int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *data)
 {
 	u32 mask;
-	unsigned timeout_ms;
+	ktime_t timeout_ns;
 	int ret;
 
 	mask = SDHCI_CMD_INHIBIT_CMD;
-	timeout_ms = SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_MS;
 
 	if (data || (cmd && (cmd->resp_type & MMC_RSP_BUSY)))
 		mask |= SDHCI_CMD_INHIBIT_DATA;
@@ -825,12 +824,10 @@ int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *da
 	if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
 		mask &= ~SDHCI_CMD_INHIBIT_DATA;
 
-	if (cmd && cmd->busy_timeout != 0)
-		timeout_ms = cmd->busy_timeout;
+	timeout_ns = sdhci_compute_timeout(cmd, data, SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_NS);
 
-	ret = wait_on_timeout(timeout_ms * MSECOND,
+	ret = wait_on_timeout(timeout_ns,
 			!(sdhci_read32(host, SDHCI_PRESENT_STATE) & mask));
-
 	if (ret) {
 		dev_err(sdhci_dev(host),
 				"SDHCI timeout while waiting for idle\n");
@@ -843,19 +840,19 @@ int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *da
 int sdhci_wait_idle_data(struct sdhci *host, struct mci_cmd *cmd)
 {
 	u32 mask;
-	unsigned timeout_ms;
+	unsigned timeout_ns;
 	int ret;
 
 	mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
-	timeout_ms = SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_MS;
+	timeout_ns = SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_NS;
 
 	if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
 		mask &= ~SDHCI_CMD_INHIBIT_DATA;
 
 	if (cmd && cmd->busy_timeout != 0)
-		timeout_ms = cmd->busy_timeout;
+		timeout_ns = cmd->busy_timeout;
 
-	ret = wait_on_timeout(timeout_ms * MSECOND,
+	ret = wait_on_timeout(timeout_ns,
 			!(sdhci_read32(host, SDHCI_PRESENT_STATE) & mask));
 
 	if (ret) {
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index d3b681153134..61058ca030a8 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -3,9 +3,11 @@
 #define __MCI_SDHCI_H
 
 #include <pbl.h>
+#include <mci.h>
 #include <dma.h>
 #include <linux/iopoll.h>
 #include <linux/sizes.h>
+#include <linux/ktime.h>
 
 #define SDHCI_DMA_ADDRESS					0x00
 #define SDHCI_BLOCK_SIZE__BLOCK_COUNT				0x04
@@ -201,7 +203,7 @@
 #define SDHCI_MAX_DIV_SPEC_200	256
 #define SDHCI_MAX_DIV_SPEC_300	2046
 
-#define SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_MS 10
+#define SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_NS	(10 * NSEC_PER_MSEC)
 
 struct sdhci {
 	u32 (*read32)(struct sdhci *host, int reg);
@@ -352,4 +354,23 @@ void sdhci_set_bus_width(struct sdhci *host, int width);
 #define sdhci_read32_poll_timeout(sdhci, reg, val, cond, timeout_us) \
 	read_poll_timeout(sdhci_read32, val, cond, timeout_us, sdhci, reg)
 
+/**
+ * sdhci_compute_timeout() - compute suitable timeout for operation
+ * @cmd: MCI command being sent, can be NULL
+ * @data: MCI data being sent, can be NULL
+ * @default_timeout: fallback value
+ *
+ * Return: the number of nanoseconds to wait.
+ */
+static inline ktime_t sdhci_compute_timeout(struct mci_cmd *cmd, struct mci_data *data,
+					    ktime_t default_timeout)
+{
+	if (data && data->timeout_ns != 0)
+		return data->timeout_ns;
+	else if (cmd && cmd->busy_timeout != 0)
+		return cmd->busy_timeout * (u64)NSEC_PER_MSEC;
+	else
+		return SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_NS;
+}
+
 #endif /* __MCI_SDHCI_H */
diff --git a/include/mci.h b/include/mci.h
index 56527f956802..d3b855f530fc 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -513,6 +513,7 @@ struct mci_data {
 	unsigned flags;		/**< refer MMC_DATA_* to define direction */
 	unsigned blocks;	/**< block count to handle in this command */
 	unsigned blocksize;	/**< block size in bytes (mostly 512) */
+	unsigned timeout_ns;	/**< data timeout in ns */
 };
 
 enum mci_timing {
-- 
2.39.5




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

* [PATCH v2 12/30] mci: imx-esdhc: use unsigned types where appropriate
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (10 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 11/30] mci: sdhci: add support for struct mci_data::timeout_ns Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 13/30] mci: imx-esdhc: implement esdhc_poll using sdhci_read32_poll_timeout Ahmad Fatoum
                   ` (18 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

clock rate and register values are both unsigned quantities, so
use the proper types for them.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 923dae9cf3b0..2e6066b20308 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -48,7 +48,7 @@ static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
 {
 	int div, pre_div, ddr_pre_div = 1;
 	struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
-	int sdhc_clk = clk_get_rate(host->clk);
+	unsigned sdhc_clk = clk_get_rate(host->clk);
 	u32 clk;
 	unsigned long  cur_clock;
 
@@ -199,7 +199,7 @@ static int esdhc_card_present(struct mci_host *mci)
 
 static int esdhc_reset(struct fsl_esdhc_host *host)
 {
-	int val;
+	u32 val;
 
 	/* reset the controller */
 	sdhci_write32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
-- 
2.39.5




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

* [PATCH v2 13/30] mci: imx-esdhc: implement esdhc_poll using sdhci_read32_poll_timeout
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (11 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 12/30] mci: imx-esdhc: use unsigned types where appropriate Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 14/30] mci: imx-esdhc: drop one extra read of SDHCI_INT_STATUS Ahmad Fatoum
                   ` (17 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

esdhc_match32 is not flexible enough for use in the upcoming HS200
tuning code, so reimplement it in terms of sdhci_read32_poll_timeout
to allow custom conditions.

No functional change.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc-common.c | 62 +++++++---------------------------
 drivers/mci/imx-esdhc.c        | 15 ++++----
 drivers/mci/imx-esdhc.h        | 29 ++++++++++++++--
 3 files changed, 45 insertions(+), 61 deletions(-)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 7673220fe8d4..0743e7b74d22 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -78,49 +78,11 @@ static int esdhc_setup_data(struct fsl_esdhc_host *host, struct mci_data *data,
 	return 0;
 }
 
-static bool esdhc_match32(struct fsl_esdhc_host *host, unsigned int off,
-			  unsigned int mask, unsigned int val)
-{
-	const unsigned int reg = sdhci_read32(&host->sdhci, off) & mask;
-
-	return reg == val;
-}
-
-#ifdef __PBL__
-/*
- * Stubs to make timeout logic below work in PBL
- */
-
-#define get_time_ns()		0
-/*
- * Use time in us (approx) as a busy counter timeout value
- */
-#define is_timeout(s, t)	((s)++ > ((t) / 1024))
-
-static void __udelay(int us)
-{
-	volatile int i;
-
-	for (i = 0; i < us * 4; i++);
-}
-
-#define udelay(n)	__udelay(n)
-
-#endif
-
-int esdhc_poll(struct fsl_esdhc_host *host, unsigned int off,
-	       unsigned int mask, unsigned int val,
-	       uint64_t timeout)
-{
-	return wait_on_timeout(timeout,
-			       esdhc_match32(host, off, mask, val));
-}
-
 int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 		     struct mci_data *data)
 {
 	u32	xfertyp, mixctrl, command;
-	u32	irqstat;
+	u32	val, irqstat;
 	dma_addr_t dma = SDHCI_NO_DMA;
 	int ret;
 
@@ -159,8 +121,8 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 		      command << 16 | xfertyp);
 
 	/* Wait for the command to complete */
-	ret = esdhc_poll(host, SDHCI_INT_STATUS,
-			 SDHCI_INT_CMD_COMPLETE, SDHCI_INT_CMD_COMPLETE,
+	ret = esdhc_poll(host, SDHCI_INT_STATUS, val,
+			 val & SDHCI_INT_CMD_COMPLETE,
 			 100 * MSECOND);
 	if (ret) {
 		dev_dbg(host->dev, "timeout 1\n");
@@ -185,9 +147,9 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 		 * Poll on DATA0 line for cmd with busy signal for
 		 * timout / 10 usec since DLA polling can be insecure.
 		 */
-		ret = esdhc_poll(host, SDHCI_PRESENT_STATE,
-				 PRSSTAT_DAT0, PRSSTAT_DAT0,
-				 2500 * MSECOND);
+		ret = esdhc_poll(host, SDHCI_PRESENT_STATE, val,
+				 val & PRSSTAT_DAT0,
+				 sdhci_compute_timeout(cmd, NULL, 2500 * MSECOND));
 		if (ret) {
 			dev_err(host->dev, "timeout PRSSTAT_DAT0\n");
 			goto undo_setup_data;
@@ -210,17 +172,17 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, -1);
 
 	/* Wait for the bus to be idle */
-	ret = esdhc_poll(host, SDHCI_PRESENT_STATE,
-			 SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA, 0,
-			 SECOND);
+	ret = esdhc_poll(host, SDHCI_PRESENT_STATE, val,
+			 (val & (SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA)) == 0,
+			 sdhci_compute_timeout(cmd, data, SECOND));
 	if (ret) {
 		dev_err(host->dev, "timeout 2\n");
 		return -ETIMEDOUT;
 	}
 
-	ret = esdhc_poll(host, SDHCI_PRESENT_STATE,
-			 SDHCI_DATA_LINE_ACTIVE, 0,
-			 100 * MSECOND);
+	ret = esdhc_poll(host, SDHCI_PRESENT_STATE, val,
+			 (val & SDHCI_DATA_LINE_ACTIVE) == 0,
+			 sdhci_compute_timeout(cmd, NULL, 100 * MSECOND));
 	if (ret) {
 		dev_err(host->dev, "timeout 3\n");
 		return -ETIMEDOUT;
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 2e6066b20308..0582b6fb8dd6 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -49,7 +49,7 @@ static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
 	int div, pre_div, ddr_pre_div = 1;
 	struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
 	unsigned sdhc_clk = clk_get_rate(host->clk);
-	u32 clk;
+	u32 val, clk;
 	unsigned long  cur_clock;
 
 	if (esdhc_is_usdhc(host) && ddr)
@@ -92,8 +92,8 @@ static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
 	esdhc_clrsetbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
 			SYSCTL_CLOCK_MASK, clk);
 
-	esdhc_poll(host, SDHCI_PRESENT_STATE,
-		   PRSSTAT_SDSTB, PRSSTAT_SDSTB,
+	esdhc_poll(host, SDHCI_PRESENT_STATE, val,
+		   val & PRSSTAT_SDSTB,
 		   10 * MSECOND);
 
 	clk = SYSCTL_PEREN | SYSCTL_CKEN | SYSCTL_INITA;
@@ -101,8 +101,8 @@ static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
 	esdhc_setbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
 			clk);
 
-	esdhc_poll(host, SDHCI_CLOCK_CONTROL,
-		   SYSCTL_INITA, SYSCTL_INITA,
+	esdhc_poll(host, SDHCI_CLOCK_CONTROL, val,
+		   val & SYSCTL_INITA,
 		   10 * MSECOND);
 }
 
@@ -217,9 +217,8 @@ static int esdhc_reset(struct fsl_esdhc_host *host)
 	}
 
 	/* hardware clears the bit when it is done */
-	if (esdhc_poll(host,
-		       SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
-		       SYSCTL_RSTA, 0, 100 * MSECOND)) {
+	if (esdhc_poll(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+		       val, (val & SYSCTL_RSTA) == 0, 100 * MSECOND)) {
 		dev_err(host->dev, "Reset never completed.\n");
 		return -EIO;
 	}
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index eff556f2ff79..2930676d5328 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -10,6 +10,10 @@
 #include <errno.h>
 #include <asm/byteorder.h>
 #include <linux/bitfield.h>
+#include <linux/math.h>
+#include <linux/ktime.h>
+
+#include "sdhci.h"
 
 #define SYSCTL_INITA		0x08000000
 #define SYSCTL_TIMEOUT_MASK	0x000f0000
@@ -163,10 +167,29 @@ esdhc_setbits32(struct fsl_esdhc_host *host, unsigned int reg,
 }
 
 void esdhc_populate_sdhci(struct fsl_esdhc_host *host);
-int esdhc_poll(struct fsl_esdhc_host *host, unsigned int off,
-	       unsigned int mask, unsigned int val,
-	       uint64_t timeout);
 int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 		     struct mci_data *data);
 
+#ifdef __PBL__
+#undef	read_poll_get_time_ns
+#define read_poll_get_time_ns()		0
+/* Use time in us (approx) as a busy counter timeout value */
+#undef	read_poll_is_timeout
+#define read_poll_is_timeout(s, t)	((s)++ > ((t) / 1024))
+
+static inline void __udelay(int us)
+{
+	volatile int i;
+
+	for (i = 0; i < us * 4; i++);
+}
+
+#define udelay(n)	__udelay(n)
+
+#endif
+
+#define esdhc_poll(host, reg, val, cond, timeout_ns)	\
+	sdhci_read32_poll_timeout(&host->sdhci, reg, val, cond, \
+				  ktime_to_us(timeout_ns))
+
 #endif  /* __FSL_ESDHC_H__ */
-- 
2.39.5




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

* [PATCH v2 14/30] mci: imx-esdhc: drop one extra read of SDHCI_INT_STATUS
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (12 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 13/30] mci: imx-esdhc: implement esdhc_poll using sdhci_read32_poll_timeout Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 15/30] mci: sdhci: add cmd parameter to sdhci_transfer_* Ahmad Fatoum
                   ` (16 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Our new esdhc_poll implementation stores the register value read for
later use, so make use of it instead of reading the same register again
after the break condition has been triggered.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc-common.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 0743e7b74d22..18a9f3be7239 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -121,16 +121,14 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 		      command << 16 | xfertyp);
 
 	/* Wait for the command to complete */
-	ret = esdhc_poll(host, SDHCI_INT_STATUS, val,
-			 val & SDHCI_INT_CMD_COMPLETE,
+	ret = esdhc_poll(host, SDHCI_INT_STATUS, irqstat,
+			 irqstat & SDHCI_INT_CMD_COMPLETE,
 			 100 * MSECOND);
 	if (ret) {
 		dev_dbg(host->dev, "timeout 1\n");
 		goto undo_setup_data;
 	}
 
-	irqstat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
-
 	if (irqstat & CMD_ERR) {
 		ret = -EIO;
 		goto undo_setup_data;
-- 
2.39.5




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

* [PATCH v2 15/30] mci: sdhci: add cmd parameter to sdhci_transfer_*
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (13 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 14/30] mci: imx-esdhc: drop one extra read of SDHCI_INT_STATUS Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 16/30] mci: arasan: introduce mmc_op_tuning helper Ahmad Fatoum
                   ` (15 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

With incoming tuning support, the SDHCI flags computed inside
sdhci_transfer_* will depend on the specific MMC command that's due to
be sent, therefore add the command as parameter to the functions.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/am654-sdhci.c            |  2 +-
 drivers/mci/arasan-sdhci.c           |  2 +-
 drivers/mci/atmel-sdhci-common.c     |  2 +-
 drivers/mci/dwcmshc-sdhci.c          |  2 +-
 drivers/mci/imx-esdhc-common.c       |  4 ++--
 drivers/mci/mci-bcm2835.c            |  2 +-
 drivers/mci/rockchip-dwcmshc-sdhci.c |  2 +-
 drivers/mci/sdhci.c                  | 14 ++++++++------
 drivers/mci/sdhci.h                  | 10 ++++++----
 9 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/drivers/mci/am654-sdhci.c b/drivers/mci/am654-sdhci.c
index 20dd03b83a9a..13c8876573c7 100644
--- a/drivers/mci/am654-sdhci.c
+++ b/drivers/mci/am654-sdhci.c
@@ -490,7 +490,7 @@ static int am654_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_read_response(&host->sdhci, cmd);
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
 
-	ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+	ret = sdhci_transfer_data_dma(&host->sdhci, cmd, data, dma);
 
 error:
 	if (ret) {
diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index 879339572b11..5774928e5a97 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -293,7 +293,7 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
 
 	if (data)
-		ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+		ret = sdhci_transfer_data_dma(&host->sdhci, cmd, data, dma);
 
 error:
 	if (ret) {
diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c
index 12b71bc2ded3..a3ed2730e527 100644
--- a/drivers/mci/atmel-sdhci-common.c
+++ b/drivers/mci/atmel-sdhci-common.c
@@ -128,7 +128,7 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
 	sdhci_write32(sdhci, SDHCI_INT_STATUS, mask);
 
 	if (data)
-		sdhci_transfer_data_pio(sdhci, data);
+		sdhci_transfer_data_pio(sdhci, cmd, data);
 
 	udelay(1000);
 
diff --git a/drivers/mci/dwcmshc-sdhci.c b/drivers/mci/dwcmshc-sdhci.c
index f6d559e393d4..174cc3f76816 100644
--- a/drivers/mci/dwcmshc-sdhci.c
+++ b/drivers/mci/dwcmshc-sdhci.c
@@ -160,7 +160,7 @@ static int do_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 
 	sdhci_read_response(&host->sdhci, cmd);
 
-	ret = sdhci_transfer_data(&host->sdhci, data, dma);
+	ret = sdhci_transfer_data(&host->sdhci, cmd, data, dma);
 error:
 	if (ret) {
 		sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 18a9f3be7239..9526bcfea8c1 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -159,9 +159,9 @@ 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())
-			ret = sdhci_transfer_data_pio(&host->sdhci, data);
+			ret = sdhci_transfer_data_pio(&host->sdhci, cmd, data);
 		else
-			ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+			ret = sdhci_transfer_data_dma(&host->sdhci, cmd, data, dma);
 
 		if (ret)
 			return ret;
diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c
index ff4c6d803c32..cd4c1b866ca1 100644
--- a/drivers/mci/mci-bcm2835.c
+++ b/drivers/mci/mci-bcm2835.c
@@ -158,7 +158,7 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
 	}
 
 	if (!ret && data)
-		ret = sdhci_transfer_data_pio(&host->sdhci, data);
+		ret = sdhci_transfer_data_pio(&host->sdhci, cmd, data);
 
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF);
 	if (ret) {
diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c
index a7da64f4d7db..2f8d5ec140b5 100644
--- a/drivers/mci/rockchip-dwcmshc-sdhci.c
+++ b/drivers/mci/rockchip-dwcmshc-sdhci.c
@@ -277,7 +277,7 @@ static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_read_response(&host->sdhci, cmd);
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
 
-	ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+	ret = sdhci_transfer_data_dma(&host->sdhci, cmd, data, dma);
 
 error:
 	if (ret) {
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 84acb3d163b5..55709e831a28 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -524,8 +524,8 @@ void sdhci_teardown_data(struct sdhci *sdhci,
 		dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE);
 }
 
-int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
-			    dma_addr_t dma)
+int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_cmd *cmd,
+			    struct mci_data *data, dma_addr_t dma)
 {
 	struct device *dev = sdhci_dev(sdhci);
 	u64 start;
@@ -594,7 +594,8 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
 	return ret;
 }
 
-int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data)
+int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_cmd *cmd,
+			    struct mci_data *data)
 {
 	unsigned int block = 0;
 	u32 stat, prs;
@@ -635,7 +636,8 @@ int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data)
 	return 0;
 }
 
-int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma)
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_cmd *cmd,
+			struct mci_data *data, dma_addr_t dma)
 {
 	struct device *dev = sdhci_dev(sdhci);
 
@@ -643,9 +645,9 @@ int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t d
 		return 0;
 
 	if (dma_mapping_error(dev, dma))
-		return sdhci_transfer_data_pio(sdhci, data);
+		return sdhci_transfer_data_pio(sdhci, cmd, data);
 	else
-		return sdhci_transfer_data_dma(sdhci, data, dma);
+		return sdhci_transfer_data_dma(sdhci, cmd, data, dma);
 }
 
 int sdhci_reset(struct sdhci *sdhci, u8 mask)
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 61058ca030a8..12ab8d148144 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -326,10 +326,12 @@ void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
 void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data);
 void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data, dma_addr_t *dma);
 void sdhci_teardown_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma);
-int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma);
-int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data);
-int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
-			    dma_addr_t dma);
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_cmd *cmd,
+			struct mci_data *data, dma_addr_t dma);
+int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_cmd *cmd,
+			    struct mci_data *data);
+int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_cmd *cmd,
+			    struct mci_data *data, dma_addr_t dma);
 int sdhci_reset(struct sdhci *sdhci, u8 mask);
 u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock,
 		   unsigned int *actual_clock, unsigned int input_clock);
-- 
2.39.5




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

* [PATCH v2 16/30] mci: arasan: introduce mmc_op_tuning helper
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (14 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 15/30] mci: sdhci: add cmd parameter to sdhci_transfer_* Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 17/30] mci: imx-esdhc: flesh out register description Ahmad Fatoum
                   ` (14 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Instead of duplicating the check most tuning related places, add a
helper like Linux does that checks if a command is sending a tuning
block.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/arasan-sdhci.c | 2 +-
 include/mci.h              | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index 5774928e5a97..ceef0b63a8b9 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -280,7 +280,7 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
 
 	/* CMD19/21 generate _only_ Buffer Read Ready interrupt */
-	if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK || cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200)
+	if (mmc_op_tuning(cmd->cmdidx))
 		mask = SDHCI_INT_DATA_AVAIL;
 
 	ret = sdhci_wait_for_done(&host->sdhci, mask);
diff --git a/include/mci.h b/include/mci.h
index d3b855f530fc..af56f453704e 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -504,6 +504,12 @@ static inline void mci_setup_cmd(struct mci_cmd *p, unsigned cmd,
 	p->resp_type = response;
 }
 
+static inline bool mmc_op_tuning(u32 cmdidx)
+{
+	return cmdidx == MMC_SEND_TUNING_BLOCK ||
+	       cmdidx == MMC_SEND_TUNING_BLOCK_HS200;
+}
+
 /** data information to be used with some SD/MMC commands */
 struct mci_data {
 	union {
-- 
2.39.5




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

* [PATCH v2 17/30] mci: imx-esdhc: flesh out register description
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (15 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 16/30] mci: arasan: introduce mmc_op_tuning helper Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 18/30] mci: imx-esdhc: add support for delay/tuning properties in DT Ahmad Fatoum
                   ` (13 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

In preparation for adding HS200 tuning support to the driver, import
missing register definitions from Linux.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc-common.c | 14 ++++++++++++++
 drivers/mci/imx-esdhc.c        | 27 +++++++++++++++++++++++++++
 drivers/mci/imx-esdhc.h        | 20 ++++++++++++++++++++
 3 files changed, 61 insertions(+)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 9526bcfea8c1..5fc9b7b30c76 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -10,6 +10,20 @@
 
 #define PRSSTAT_DAT0  0x01000000
 
+#define	ESDHC_CTRL_D3CD			0x08
+
+#define  SDHCI_CTRL_VDD_180         0x0008
+
+#define ESDHC_MIX_CTRL			0x48
+#define  ESDHC_MIX_CTRL_DDREN		(1 << 3)
+#define  ESDHC_MIX_CTRL_AC23EN		(1 << 7)
+#define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22)
+#define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23)
+#define  ESDHC_MIX_CTRL_AUTO_TUNE_EN	(1 << 24)
+#define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
+#define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
+#define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
+
 static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg)
 {
 	struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 0582b6fb8dd6..86dc6daba845 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -28,6 +28,33 @@
 
 
 #define PRSSTAT_SDSTB 0x00000008
+#define ESDHC_BURST_LEN_EN_INCR		(1 << 27)
+
+/* Bits 3 and 6 are not SDHCI standard definitions */
+#define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
+/* Tuning bits */
+#define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
+
+/* dll control register */
+#define ESDHC_DLL_CTRL			0x60
+#define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9
+#define ESDHC_DLL_OVERRIDE_EN_SHIFT	8
+
+/* tune control register */
+#define ESDHC_TUNE_CTRL_STATUS		0x68
+#define  ESDHC_TUNE_CTRL_STEP		1
+#define  ESDHC_TUNE_CTRL_MIN		0
+#define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
+
+#define ESDHC_TUNING_CTRL		0xcc
+#define ESDHC_STD_TUNING_EN		(1 << 24)
+/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
+#define ESDHC_TUNING_START_TAP_DEFAULT	0x1
+#define ESDHC_TUNING_START_TAP_MASK	0x7f
+#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE	(1 << 7)
+#define ESDHC_TUNING_STEP_DEFAULT	0x1
+#define ESDHC_TUNING_STEP_MASK		0x00070000
+#define ESDHC_TUNING_STEP_SHIFT		16
 
 
 #define to_fsl_esdhc(mci)	container_of(mci, struct fsl_esdhc_host, mci)
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index 2930676d5328..ea57ca534f80 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -64,6 +64,26 @@
 #define IMX_SDHCI_DLL_CTRL	0x60
 #define IMX_SDHCI_MIX_CTRL_FBCLK_SEL	BIT(25)
 
+/* tune control register */
+#define ESDHC_TUNE_CTRL_STATUS		0x68
+#define  ESDHC_TUNE_CTRL_STEP		1
+#define  ESDHC_TUNE_CTRL_MIN		0
+#define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
+
+/* VENDOR SPEC register */
+#define ESDHC_VENDOR_SPEC		0xc0
+#define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1)
+#define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1)
+#define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)
+
+#define ESDHC_VEND_SPEC2		0xc8
+#define ESDHC_VEND_SPEC2_EN_BUSY_IRQ	(1 << 8)
+#define ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN	(1 << 4)
+#define ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN	(0 << 4)
+#define ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN	(2 << 4)
+#define ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN	(1 << 6)
+#define ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK	(7 << 4)
+
 #define ESDHC_DMA_SYSCTL	0x40c /* Layerscape specific */
 #define ESDHC_SYSCTL_DMA_SNOOP 	BIT(6)
 #define ESDHC_SYSCTL_PERIPHERAL_CLK_SEL	BIT(19)
-- 
2.39.5




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

* [PATCH v2 18/30] mci: imx-esdhc: add support for delay/tuning properties in DT
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (16 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 17/30] mci: imx-esdhc: flesh out register description Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 19/30] mci: add mci_set_timing helper Ahmad Fatoum
                   ` (12 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

With higher data rates, we will need to fine tune delay settings, which
can be supplied via the device tree. Add support for the binding to
barebox.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc.c | 59 ++++++++++++++++++++++++++++++++++++++++-
 drivers/mci/imx-esdhc.h | 14 ++++++++++
 2 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 86dc6daba845..5416d0648fa8 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -145,6 +145,15 @@ static void usdhc_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing
 	case MMC_TIMING_MMC_DDR52:
 		mixctrl |= MIX_CTRL_DDREN;
 		sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+		if (host->boarddata.delay_line) {
+			u32 v;
+			v = host->boarddata.delay_line <<
+				IMX_SDHCI_DLL_CTRL_OVERRIDE_VAL_SHIFT |
+				(1 << IMX_SDHCI_DLL_CTRL_OVERRIDE_EN_SHIFT);
+			if (cpu_is_mx53())
+				v <<= 1;
+			sdhci_write32(&host->sdhci, IMX_SDHCI_DLL_CTRL, v);
+		}
 		break;
 	default:
 		sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
@@ -290,7 +299,36 @@ static int esdhc_init(struct mci_host *mci, struct device *dev)
 	esdhc_clrsetbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
 			SYSCTL_TIMEOUT_MASK, 14 << 16);
 
-	return ret;
+	if (IS_ENABLED(CONFIG_MCI_TUNING) && esdhc_is_usdhc(host) &&
+	    (host->socdata->flags & ESDHC_FLAG_STD_TUNING)) {
+		u32 tmp;
+
+		/* disable DLL_CTRL delay line settings */
+		sdhci_write32(&host->sdhci, ESDHC_DLL_CTRL, 0x0);
+
+		tmp = sdhci_read32(&host->sdhci, ESDHC_TUNING_CTRL);
+		tmp |= ESDHC_STD_TUNING_EN;
+
+		tmp &= ~(ESDHC_TUNING_START_TAP_MASK | ESDHC_TUNING_STEP_MASK);
+		tmp |= host->boarddata.tuning_start_tap;
+
+		tmp |= host->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT;
+
+		/* Disable the CMD CRC check for tuning, if not, need to
+		 * add some delay after every tuning command, because
+		 * hardware standard tuning logic will directly go to next
+		 * step once it detect the CMD CRC error, will not wait for
+		 * the card side to finally send out the tuning data, trigger
+		 * the buffer read ready interrupt immediately. If usdhc send
+		 * the next tuning command some eMMC card will stuck, can't
+		 * response, block the tuning procedure or the first command
+		 * after the whole tuning procedure always can't get any response.
+		 */
+		tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
+		sdhci_write32(&host->sdhci, ESDHC_TUNING_CTRL, tmp);
+	}
+
+	return 0;
 }
 
 static const struct mci_ops fsl_esdhc_ops = {
@@ -300,6 +338,23 @@ static const struct mci_ops fsl_esdhc_ops = {
 	.card_present = esdhc_card_present,
 };
 
+static void fsl_esdhc_probe_dt(struct device *dev, struct fsl_esdhc_host *host)
+{
+	struct device_node *np = dev->of_node;
+	struct esdhc_platform_data *boarddata = &host->boarddata;
+
+	if (!IS_ENABLED(CONFIG_MCI_TUNING))
+		return;
+
+	if (of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step))
+		boarddata->tuning_step = ESDHC_TUNING_STEP_DEFAULT;
+	if (of_property_read_u32(np, "fsl,tuning-start-tap",
+			     &boarddata->tuning_start_tap))
+		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;
+}
+
 static int fsl_esdhc_probe(struct device *dev)
 {
 	struct resource *iores;
@@ -360,6 +415,8 @@ static int fsl_esdhc_probe(struct device *dev)
 
 	mci_of_parse(&host->mci);
 
+	fsl_esdhc_probe_dt(dev, host);
+
 	ret = mci_register(&host->mci);
 	if (ret)
 		goto err_release_res;
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index ea57ca534f80..569986c1bf0e 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -62,6 +62,8 @@
 /* Tuning bits */
 #define  MIX_CTRL_TUNING_MASK	0x03c00000
 #define IMX_SDHCI_DLL_CTRL	0x60
+#define  IMX_SDHCI_DLL_CTRL_OVERRIDE_VAL_SHIFT	9
+#define  IMX_SDHCI_DLL_CTRL_OVERRIDE_EN_SHIFT	8
 #define IMX_SDHCI_MIX_CTRL_FBCLK_SEL	BIT(25)
 
 /* tune control register */
@@ -137,11 +139,23 @@ struct esdhc_soc_data {
 	const char *clkidx;
 };
 
+/*
+ * struct esdhc_platform_data - platform data for esdhc on i.MX
+ */
+
+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 */
+};
+
 struct fsl_esdhc_host {
 	struct mci_host		mci;
 	struct clk		*clk;
 	struct device		*dev;
 	const struct esdhc_soc_data *socdata;
+	struct esdhc_platform_data boarddata;
+	u32		last_cmd;
 	struct sdhci	sdhci;
 };
 
-- 
2.39.5




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

* [PATCH v2 19/30] mci: add mci_set_timing helper
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (17 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 18/30] mci: imx-esdhc: add support for delay/tuning properties in DT Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:21 ` [PATCH v2 20/30] mci: imx-esdhc: add support for setting drive strength Ahmad Fatoum
                   ` (11 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

HS400 support when it goes in will add a number of extra calls to
mci_set_ios after changing just the timing. We already have two places
where we do this, so let's have a helper for it like Linux does.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/mci-core.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 2f80763d9831..f123fce59cb1 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1093,6 +1093,19 @@ static void mci_set_bus_width(struct mci *mci, enum mci_bus_width width)
 	mci_set_ios(mci);
 }
 
+/**
+ * Setup host's interface timing
+ * @param mci MCI instance
+ * @param width New timing
+ */
+static void mci_set_timing(struct mci *mci, enum mci_timing timing)
+{
+	struct mci_host *host = mci->host;
+
+	host->ios.timing = timing;
+	mci_set_ios(mci);
+}
+
 /**
  * Extract card's version from its CSD
  * @param mci MCI instance
@@ -1709,8 +1722,7 @@ static int mmc_select_hs200(struct mci *mci)
 		old_timing = mci->host->ios.timing;
 		old_clock = mci->host->ios.clock;
 
-		mci->host->ios.timing = MMC_TIMING_MMC_HS200;
-		mci_set_ios(mci);
+		mci_set_timing(mci, MMC_TIMING_MMC_HS200);
 		mci_set_clock(mci, mci->host->hs_max_dtr);
 
 		err = mci_switch_status(mci, true);
@@ -1721,8 +1733,7 @@ static int mmc_select_hs200(struct mci *mci)
 		 */
 		if (err == -EBADMSG) {
 			mci->host->ios.clock = old_clock;
-			mci->host->ios.timing = old_timing;
-			mci_set_ios(mci);
+			mci_set_timing(mci, old_timing);
 		}
 	}
 err:
-- 
2.39.5




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

* [PATCH v2 20/30] mci: imx-esdhc: add support for setting drive strength
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (18 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 19/30] mci: add mci_set_timing helper Ahmad Fatoum
@ 2025-05-07  8:21 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 21/30] mci: sdhci: move SDHCI_MAKE_BLKSZ definition to header Ahmad Fatoum
                   ` (10 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:21 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

With increasing clock rates, drive strength may need to be reduced. Add
support for that to the i.MX eSDHC driver and to the core.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc.c |  2 ++
 drivers/mci/mci-core.c  | 40 ++++++++++++++++++++++++++++++++++++++--
 drivers/mci/sdhci.c     | 29 +++++++++++++++++++++++++++++
 drivers/mci/sdhci.h     |  6 ++++++
 include/mci.h           | 10 ++++++++++
 5 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 5416d0648fa8..3871f85ea6d6 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -207,6 +207,8 @@ static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
 	/* Set the clock speed */
 	set_sysctl(mci, ios->clock, mci_timing_is_ddr(ios->timing));
 
+	sdhci_set_drv_type(&host->sdhci, ios->drv_type);
+
 	/* Set the bus width */
 	esdhc_clrbits32(host, SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
 			PROCTL_DTW_4 | PROCTL_DTW_8);
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index f123fce59cb1..80b3496a280a 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1626,6 +1626,28 @@ int mci_send_abort_tuning(struct mci *mci, u32 opcode)
 }
 EXPORT_SYMBOL_GPL(mci_send_abort_tuning);
 
+static void mmc_select_driver_type(struct mci *mci)
+{
+	int card_drv_type, drive_strength;
+	int fixed_drv_type = mci->host->fixed_drv_type;
+
+	card_drv_type = mci->ext_csd[EXT_CSD_DRIVER_STRENGTH] |
+			mmc_driver_type_mask(0);
+
+	if (mci->host->fixed_drv_type_valid)
+		drive_strength = card_drv_type & mmc_driver_type_mask(fixed_drv_type)
+				 ? fixed_drv_type : 0;
+	else
+		drive_strength = 0;
+
+	mci->host->drive_strength = drive_strength;
+
+	/* Linux only sets the drive strength immediately if the driver
+	 * implements select_drive_strength, which none of our drivers
+	 * do yet
+	 */
+}
+
 static void mmc_select_max_dtr(struct mci *mci)
 {
 	u8 card_type = mci->ext_csd[EXT_CSD_DEVICE_TYPE];
@@ -1698,6 +1720,8 @@ static int mmc_select_hs200(struct mci *mci)
 	int err = -EINVAL;
 	u8 val;
 
+	mmc_select_driver_type(mci);
+
 	/*
 	 * Set the bus width(4 or 8) with host's support and
 	 * switch to HS200 mode if bus width is set successfully.
@@ -1705,8 +1729,7 @@ static int mmc_select_hs200(struct mci *mci)
 	/* find out maximum bus width and then try DDR if supported */
 	err = mci_mmc_select_bus_width(mci);
 	if (err > 0) {
-		/* TODO  actually set drive strength instead of 0. Currently unsupported. */
-		val = EXT_CSD_TIMING_HS200 | 0 << EXT_CSD_DRV_STR_SHIFT;
+		val = EXT_CSD_TIMING_HS200 | (mci->host->drive_strength << EXT_CSD_DRV_STR_SHIFT);
 		err = mci_switch(mci, EXT_CSD_HS_TIMING, val);
 		if (err == -EIO)
 			return -EBADMSG;
@@ -3095,6 +3118,8 @@ void mci_of_parse_node(struct mci_host *host,
 	if (of_property_read_bool(np, "no-mmc"))
 		host->caps2 |= MMC_CAP2_NO_MMC;
 	if (IS_ENABLED(CONFIG_MCI_TUNING)) {
+		u32 drv_type;
+
 		if (of_property_read_bool(np, "mmc-hs200-1_8v"))
 			host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
 		if (of_property_read_bool(np, "mmc-hs200-1_2v"))
@@ -3119,6 +3144,17 @@ void mci_of_parse_node(struct mci_host *host,
 			 */
 			host->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES);
 		}
+
+		/* Must be after "non-removable" check */
+		if (of_property_read_u32(np, "fixed-emmc-driver-type", &drv_type) == 0) {
+			if (host->non_removable) {
+				host->fixed_drv_type = drv_type;
+				host->fixed_drv_type_valid = true;
+			 } else {
+				dev_err(host->hw_dev,
+					"can't use fixed driver type, media is removable\n");
+			 }
+		}
 	}
 }
 
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 55709e831a28..b86b3f3d8d1c 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -887,6 +887,35 @@ void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_
 	sdhci_enable_clk(host, clk);
 }
 
+void sdhci_set_drv_type(struct sdhci *host, unsigned drv_type)
+{
+	u16 ctrl_2;
+
+	if (host->preset_enabled)
+		return;
+
+	/*
+	 * We only need to set Driver Strength if the
+	 * preset value enable is not set.
+	 */
+	ctrl_2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+	ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
+	if (drv_type == MMC_SET_DRIVER_TYPE_A)
+		ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
+	else if (drv_type == MMC_SET_DRIVER_TYPE_B)
+		ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
+	else if (drv_type == MMC_SET_DRIVER_TYPE_C)
+		ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
+	else if (drv_type == MMC_SET_DRIVER_TYPE_D)
+		ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D;
+	else {
+		dev_warn(sdhci_dev(host), "invalid driver type, default to driver type B\n");
+		ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
+	}
+
+	sdhci_write16(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
 static void sdhci_do_enable_v4_mode(struct sdhci *host)
 {
 	u16 ctrl2;
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 12ab8d148144..31edebab1262 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -133,6 +133,11 @@
 #define   SDHCI_CTRL_UHS_SDR104			0x3
 #define   SDHCI_CTRL_UHS_DDR50			0x4
 #define   SDHCI_CTRL_HS400			0x5 /* Non-standard */
+#define  SDHCI_CTRL_DRV_TYPE_MASK		GENMASK(5, 4)
+#define   SDHCI_CTRL_DRV_TYPE_B			0x0000
+#define   SDHCI_CTRL_DRV_TYPE_A			0x0010
+#define   SDHCI_CTRL_DRV_TYPE_C			0x0020
+#define   SDHCI_CTRL_DRV_TYPE_D			0x0030
 #define  SDHCI_CTRL_EXEC_TUNING			BIT(6)
 #define  SDHCI_CTRL_TUNED_CLK			BIT(7)
 #define  SDHCI_CTRL_64BIT_ADDR			BIT(13)
@@ -337,6 +342,7 @@ u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock,
 		   unsigned int *actual_clock, unsigned int input_clock);
 void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock);
 void sdhci_enable_clk(struct sdhci *host, u16 clk);
+void sdhci_set_drv_type(struct sdhci *host, unsigned drv_type);
 void sdhci_enable_v4_mode(struct sdhci *host);
 int sdhci_setup_host(struct sdhci *host);
 void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
diff --git a/include/mci.h b/include/mci.h
index af56f453704e..29aaecb46305 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -208,6 +208,8 @@
 #define SD_ERASE_ARG			0x00000000
 #define SD_DISCARD_ARG			0x00000001
 
+#define mmc_driver_type_mask(n)		(1 << (n))
+
 /*
  * EXT_CSD fields
  */
@@ -560,6 +562,7 @@ struct mci_ios {
 	unsigned int		clock;			/* clock rate */
 	enum mci_bus_width	bus_width;		/* data bus width */
 	enum mci_timing		timing;			/* timing specification used */
+	unsigned char		drv_type;		/* driver type (A, B, C, D) */
 };
 
 struct mci;
@@ -632,6 +635,13 @@ struct mci_host {
 	int broken_cd;		/**< card detect is broken */
 	bool non_removable;	/**< device is non removable */
 	bool disable_wp;	/**< ignore write-protect detection logic */
+	bool fixed_drv_type_valid;
+	unsigned char fixed_drv_type;	/**< fixed driver type for non-removable media */
+	unsigned char	drive_strength;	/**< driver type (A, B, C, D) */
+#define MMC_SET_DRIVER_TYPE_B	0
+#define MMC_SET_DRIVER_TYPE_A	1
+#define MMC_SET_DRIVER_TYPE_C	2
+#define MMC_SET_DRIVER_TYPE_D	3
 	struct regulator *supply;
 	struct mci_ops ops;
 };
-- 
2.39.5




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

* [PATCH v2 21/30] mci: sdhci: move SDHCI_MAKE_BLKSZ definition to header
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (19 preceding siblings ...)
  2025-05-07  8:21 ` [PATCH v2 20/30] mci: imx-esdhc: add support for setting drive strength Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 22/30] mci: imx-esdhc: select different pinctrl state depending on frequency Ahmad Fatoum
                   ` (9 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

This is going to be used outside of sdhci.c, so move it into the header.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/sdhci.c | 1 -
 drivers/mci/sdhci.h | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index b86b3f3d8d1c..3051354a3076 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -10,7 +10,6 @@
 #include "sdhci.h"
 
 #define MAX_TUNING_LOOP 40
-#define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
 
 enum sdhci_reset_reason {
 	SDHCI_RESET_FOR_INIT,
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 31edebab1262..e0436425fbda 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -25,6 +25,7 @@
 #define  SDHCI_DEFAULT_BOUNDARY_ARG		SDHCI_DMA_BOUNDARY_512K
 #define  SDHCI_TRANSFER_BLOCK_SIZE(x)		((x) & 0xfff)
 #define SDHCI_BLOCK_COUNT					0x06
+#define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
 #define SDHCI_ARGUMENT						0x08
 #define SDHCI_TRANSFER_MODE__COMMAND				0x0c
 #define SDHCI_TRANSFER_MODE					0x0c
-- 
2.39.5




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

* [PATCH v2 22/30] mci: imx-esdhc: select different pinctrl state depending on frequency
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (20 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 21/30] mci: sdhci: move SDHCI_MAKE_BLKSZ definition to header Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 23/30] mci: core: retry MMC_CMD_SET_BLOCKLEN up to 4 times Ahmad Fatoum
                   ` (8 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Like done before for the card side, we need to adjust drive strength on
the host as well with higher data rates. For i.MX, this is done via
speed-specific pincontrol groups, so implement this binding.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc.c | 48 +++++++++++++++++++++++++++++++++++++++++
 drivers/mci/imx-esdhc.h |  3 +++
 2 files changed, 51 insertions(+)

diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 3871f85ea6d6..18a5750ed0ed 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -14,6 +14,7 @@
 #include <of.h>
 #include <malloc.h>
 #include <mci.h>
+#include <linux/pinctrl/consumer.h>
 #include <clock.h>
 #include <io.h>
 #include <linux/clk.h>
@@ -56,6 +57,9 @@
 #define ESDHC_TUNING_STEP_MASK		0x00070000
 #define ESDHC_TUNING_STEP_SHIFT		16
 
+/* pinctrl state */
+#define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
+#define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
 
 #define to_fsl_esdhc(mci)	container_of(mci, struct fsl_esdhc_host, mci)
 
@@ -133,6 +137,37 @@ static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
 		   10 * MSECOND);
 }
 
+static int esdhc_change_pinstate(struct fsl_esdhc_host *host,
+				 unsigned int uhs)
+{
+	struct pinctrl_state *pinctrl;
+
+	dev_dbg(host->dev, "change pinctrl state for uhs %d\n", uhs);
+
+	if (IS_ERR(host->pinctrl) ||
+		IS_ERR(host->pins_100mhz) ||
+		IS_ERR(host->pins_200mhz))
+		return -EINVAL;
+
+	switch (uhs) {
+	case MMC_TIMING_UHS_SDR50:
+	case MMC_TIMING_UHS_DDR50:
+		pinctrl = host->pins_100mhz;
+		break;
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_MMC_HS200:
+	case MMC_TIMING_MMC_HS400:
+		pinctrl = host->pins_200mhz;
+		break;
+	default:
+		/* back to default state for other legacy timing */
+		return pinctrl_select_state_default(host->dev);
+	}
+
+	return pinctrl_select_state(host->pinctrl, pinctrl);
+}
+
+
 static void usdhc_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing)
 {
 	u32 mixctrl;
@@ -159,6 +194,8 @@ static void usdhc_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing
 		sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
 	}
 
+	esdhc_change_pinstate(host, timing);
+
 	host->sdhci.timing = timing;
 }
 
@@ -355,6 +392,13 @@ 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 (esdhc_is_usdhc(host) && !IS_ERR(host->pinctrl)) {
+		host->pins_100mhz = pinctrl_lookup_state(host->pinctrl,
+						ESDHC_PINCTRL_STATE_100MHZ);
+		host->pins_200mhz = pinctrl_lookup_state(host->pinctrl,
+						ESDHC_PINCTRL_STATE_200MHZ);
+	}
 }
 
 static int fsl_esdhc_probe(struct device *dev)
@@ -417,6 +461,10 @@ static int fsl_esdhc_probe(struct device *dev)
 
 	mci_of_parse(&host->mci);
 
+	host->pinctrl = pinctrl_get(dev);
+	if (IS_ERR(host->pinctrl))
+		dev_warn(host->dev, "could not get pinctrl\n");
+
 	fsl_esdhc_probe_dt(dev, host);
 
 	ret = mci_register(&host->mci);
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index 569986c1bf0e..e24d76d0c687 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -153,6 +153,9 @@ struct fsl_esdhc_host {
 	struct mci_host		mci;
 	struct clk		*clk;
 	struct device		*dev;
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*pins_100mhz;
+	struct pinctrl_state	*pins_200mhz;
 	const struct esdhc_soc_data *socdata;
 	struct esdhc_platform_data boarddata;
 	u32		last_cmd;
-- 
2.39.5




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

* [PATCH v2 23/30] mci: core: retry MMC_CMD_SET_BLOCKLEN up to 4 times
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (21 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 22/30] mci: imx-esdhc: select different pinctrl state depending on frequency Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 24/30] mci: imx-esdhc: don't reconfigure clock unless required Ahmad Fatoum
                   ` (7 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

This is what Linux is doing, so follow suit for reliability.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/mci-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 80b3496a280a..5f0fdd2206a5 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -125,7 +125,7 @@ static int mci_set_blocklen(struct mci *mci, unsigned len)
 		return 0;
 
 	mci_setup_cmd(&cmd, MMC_CMD_SET_BLOCKLEN, len, MMC_RSP_R1);
-	return mci_send_cmd(mci, &cmd, NULL);
+	return mci_send_cmd_retry(mci, &cmd, NULL, 4);
 }
 
 static void *sector_buf;
-- 
2.39.5




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

* [PATCH v2 24/30] mci: imx-esdhc: don't reconfigure clock unless required
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (22 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 23/30] mci: core: retry MMC_CMD_SET_BLOCKLEN up to 4 times Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 25/30] mci: sdhci: fix sdhci_transfer_data MMC_SEND_TUNING compatibility Ahmad Fatoum
                   ` (6 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

The Linux SDHCI driver skips sdhci_ops::set_clock if the clock rate is
not going to change. Let's do the same in barebox with the slight
difference that we also check for whether DDR settings have changed,
because that influences the clock configuration on the eSDHC.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 18a5750ed0ed..2bdd1532fe74 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -226,6 +226,7 @@ static void layerscape_set_timing(struct fsl_esdhc_host *host, enum mci_timing t
 static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
 {
 	struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
+	bool ddr_changed = false;
 
 	/*
 	 * call esdhc_set_timing() before update the clock rate,
@@ -235,14 +236,18 @@ static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
 	 * setting clock rate.
 	 */
 	if (host->sdhci.timing != ios->timing) {
+		if (mci_timing_is_ddr(host->sdhci.timing) != mci_timing_is_ddr(ios->timing))
+			ddr_changed = true;
+
 		if (esdhc_is_usdhc(host))
 			usdhc_set_timing(host, ios->timing);
 		else if (esdhc_is_layerscape(host))
 			layerscape_set_timing(host, ios->timing);
 	}
 
-	/* Set the clock speed */
-	set_sysctl(mci, ios->clock, mci_timing_is_ddr(ios->timing));
+	/* Reconfigure clock if requested speed changes */
+	if (!ios->clock || mci->actual_clock != ios->clock || ddr_changed)
+		set_sysctl(mci, ios->clock, mci_timing_is_ddr(ios->timing));
 
 	sdhci_set_drv_type(&host->sdhci, ios->drv_type);
 
-- 
2.39.5




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

* [PATCH v2 25/30] mci: sdhci: fix sdhci_transfer_data MMC_SEND_TUNING compatibility
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (23 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 24/30] mci: imx-esdhc: don't reconfigure clock unless required Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 26/30] mci: core: implement mmc_send_tuning Ahmad Fatoum
                   ` (5 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Depending on whether we are executing a tuning sequence or not, we need
to poll for different status bits. Implement the logic in preparation
for adding HS200 support to the i.MX eSDHC driver.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/sdhci.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 3051354a3076..c8fd78a5e62d 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -291,8 +291,10 @@ void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
 		if (data->flags & MMC_DATA_READ)
 			*xfer |= SDHCI_DATA_TO_HOST;
 
-		if (dma)
+		if (dma && !mmc_op_tuning(cmd->cmdidx))
 			*xfer |= SDHCI_DMA_EN;
+	} else if (mmc_op_tuning(cmd->cmdidx)) {
+		*command |= SDHCI_DATA_PRESENT;
 	}
 }
 
@@ -529,7 +531,7 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_cmd *cmd,
 	struct device *dev = sdhci_dev(sdhci);
 	u64 start;
 	int nbytes;
-	u32 irqstat;
+	u32 irqcheck, irqstat;
 	int ret;
 
 	if (!data)
@@ -539,6 +541,10 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_cmd *cmd,
 
 	start = get_time_ns();
 
+	irqcheck = SDHCI_INT_XFER_COMPLETE;
+	if (mmc_op_tuning(cmd->cmdidx))
+		irqcheck = SDHCI_INT_DATA_AVAIL;
+
 	do {
 		irqstat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
 
@@ -575,7 +581,7 @@ int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_cmd *cmd,
 			sdhci_set_sdma_addr(sdhci, ALIGN(dma, SDHCI_DEFAULT_BOUNDARY_SIZE));
 		}
 
-		if (irqstat & SDHCI_INT_XFER_COMPLETE)
+		if (irqstat & irqcheck)
 			break;
 
 		if (is_timeout(start, 10 * SECOND)) {
@@ -597,12 +603,16 @@ int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_cmd *cmd,
 			    struct mci_data *data)
 {
 	unsigned int block = 0;
-	u32 stat, prs;
+	u32 stat, prs, irqcheck;
 	uint64_t start = get_time_ns();
 
 	if (!data)
 		return 0;
 
+	irqcheck = SDHCI_INT_XFER_COMPLETE;
+	if (mmc_op_tuning(cmd->cmdidx))
+		irqcheck = SDHCI_INT_DATA_AVAIL;
+
 	do {
 		stat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
 		if (stat & SDHCI_INT_ERROR)
@@ -630,7 +640,7 @@ int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_cmd *cmd,
 		if (is_timeout(start, 10 * SECOND))
 			return -ETIMEDOUT;
 
-	} while (!(stat & SDHCI_INT_XFER_COMPLETE));
+	} while (!(stat & irqcheck));
 
 	return 0;
 }
@@ -822,7 +832,8 @@ int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *da
 	if (data || (cmd && (cmd->resp_type & MMC_RSP_BUSY)))
 		mask |= SDHCI_CMD_INHIBIT_DATA;
 
-	if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+	if (cmd && (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION ||
+		    mmc_op_tuning(cmd->cmdidx)))
 		mask &= ~SDHCI_CMD_INHIBIT_DATA;
 
 	timeout_ns = sdhci_compute_timeout(cmd, data, SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_NS);
@@ -847,7 +858,8 @@ int sdhci_wait_idle_data(struct sdhci *host, struct mci_cmd *cmd)
 	mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
 	timeout_ns = SDHCI_CMD_DEFAULT_BUSY_TIMEOUT_NS;
 
-	if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+	if (cmd && (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION ||
+		    mmc_op_tuning(cmd->cmdidx)))
 		mask &= ~SDHCI_CMD_INHIBIT_DATA;
 
 	if (cmd && cmd->busy_timeout != 0)
-- 
2.39.5




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

* [PATCH v2 26/30] mci: core: implement mmc_send_tuning
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (24 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 25/30] mci: sdhci: fix sdhci_transfer_data MMC_SEND_TUNING compatibility Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 27/30] mci: imx-esdhc: set burst_length_enable Ahmad Fatoum
                   ` (4 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

For use by drivers that support eMMC standard tuning, add a new
mmc_send_tuning helper that sends 64-128 bytes and checks the result
for bit errors.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/mci-core.c | 78 ++++++++++++++++++++++++++++++++++++++++++
 include/mci.h          |  1 +
 2 files changed, 79 insertions(+)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 5f0fdd2206a5..23cde58c0c5b 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1607,6 +1607,84 @@ int mci_execute_tuning(struct mci *mci)
 	return host->ops.execute_tuning(host, opcode);
 }
 
+static const u8 tuning_blk_pattern_4bit[] = {
+	0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
+	0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+	0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+	0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+	0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+	0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+	0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+	0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
+};
+
+static const u8 tuning_blk_pattern_8bit[] = {
+	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
+	0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
+	0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
+	0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
+	0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
+	0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
+	0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
+	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
+	0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
+	0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
+	0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
+	0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
+	0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
+	0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
+	0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
+};
+
+int mmc_send_tuning(struct mci *mci, u32 opcode)
+{
+	struct mci_cmd cmd = {};
+	struct mci_data data = {};
+	const u8 *tuning_block_pattern;
+	int size, err = 0;
+	u8 *data_buf;
+
+	if (mci->host->ios.bus_width == MMC_BUS_WIDTH_8) {
+		tuning_block_pattern = tuning_blk_pattern_8bit;
+		size = sizeof(tuning_blk_pattern_8bit);
+	} else if (mci->host->ios.bus_width == MMC_BUS_WIDTH_4) {
+		tuning_block_pattern = tuning_blk_pattern_4bit;
+		size = sizeof(tuning_blk_pattern_4bit);
+	} else
+		return -EINVAL;
+
+	data_buf = calloc(size, 1);
+	if (!data_buf)
+		return -ENOMEM;
+
+	mci_setup_cmd(&cmd, opcode, 0, MMC_RSP_R1 | MMC_CMD_ADTC);
+
+
+	data.blocksize = size;
+	data.blocks = 1;
+	data.flags = MMC_DATA_READ;
+
+	/*
+	 * According to the tuning specs, Tuning process
+	 * is normally shorter 40 executions of CMD19,
+	 * and timeout value should be shorter than 150 ms
+	 */
+	data.timeout_ns = 150 * NSEC_PER_MSEC;
+
+	err = mci_send_cmd(mci, &cmd, &data);
+	if (err)
+		goto out;
+
+	if (memcmp(data_buf, tuning_block_pattern, size))
+		err = -EIO;
+
+out:
+	free(data_buf);
+	return err;
+}
+EXPORT_SYMBOL_GPL(mmc_send_tuning);
+
 int mci_send_abort_tuning(struct mci *mci, u32 opcode)
 {
 	struct mci_cmd cmd = {};
diff --git a/include/mci.h b/include/mci.h
index 29aaecb46305..207fb9779a14 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -773,6 +773,7 @@ static inline int mmc_card_hs(struct mci *mci)
  */
 int mmc_hs200_tuning(struct mci *mci);
 int mci_execute_tuning(struct mci *mci);
+int mmc_send_tuning(struct mci *mci, u32 opcode);
 int mci_send_abort_tuning(struct mci *mci, u32 opcode);
 int mmc_select_timing(struct mci *mci);
 int mci_set_blockcount(struct mci *mci, unsigned int cmdarg);
-- 
2.39.5




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

* [PATCH v2 27/30] mci: imx-esdhc: set burst_length_enable
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (25 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 26/30] mci: core: implement mmc_send_tuning Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 28/30] mci: imx-esdhc: fixup quirks in standard SDHCI registers Ahmad Fatoum
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Linux writes this register unconditionally for all usdhc. As we didn't
seem to be hurt by its lack so far, we will set it only when
tuning support is enabled.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 2bdd1532fe74..00b7e0693fcc 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -347,6 +347,21 @@ static int esdhc_init(struct mci_host *mci, struct device *dev)
 	    (host->socdata->flags & ESDHC_FLAG_STD_TUNING)) {
 		u32 tmp;
 
+		/*
+		 * ROM code will change the bit burst_length_enable setting
+		 * to zero if this usdhc is chosen to boot system. Change
+		 * it back here, otherwise it will impact the performance a
+		 * lot. This bit is used to enable/disable the burst length
+		 * for the external AHB2AXI bridge. It's useful especially
+		 * for INCR transfer because without burst length indicator,
+		 * the AHB2AXI bridge does not know the burst length in
+		 * advance. And without burst length indicator, AHB INCR
+		 * transfer can only be converted to singles on the AXI side.
+		 */
+		sdhci_write32(&host->sdhci, SDHCI_HOST_CONTROL,
+			      sdhci_read32(&host->sdhci, SDHCI_HOST_CONTROL)
+			| ESDHC_BURST_LEN_EN_INCR);
+
 		/* disable DLL_CTRL delay line settings */
 		sdhci_write32(&host->sdhci, ESDHC_DLL_CTRL, 0x0);
 
-- 
2.39.5




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

* [PATCH v2 28/30] mci: imx-esdhc: fixup quirks in standard SDHCI registers
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (26 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 27/30] mci: imx-esdhc: set burst_length_enable Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 29/30] mci: sdhci: support Linux SDHCI_QUIRK2_BROKEN_HS200 flag Ahmad Fatoum
                   ` (2 subsequent siblings)
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

The eSDHC implementation differs from the spec sufficiently to break the
generic SDHCI tuning support. Linux works around this by intercepting
some particular register reads and faking the content.

Let's begrudgingly do the same for barebox.

Mildly interesting statistic: As of Linux v6.12, the generic SDHCI driver
defines 51 quirk bits and 32 callbacks to handle differences.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc-common.c | 229 +++++++++++++++++++++++++++++++++
 drivers/mci/imx-esdhc.h        |  30 +++++
 drivers/mci/sdhci.h            |   1 +
 3 files changed, 260 insertions(+)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 5fc9b7b30c76..caa8505ac00e 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -23,6 +23,10 @@
 #define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
 #define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
 #define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
+/* Bits 3 and 6 are not SDHCI standard definitions */
+#define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
+/* Tuning bits */
+#define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
 
 static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg)
 {
@@ -52,6 +56,224 @@ static void esdhc_op_write16_be(struct sdhci *sdhci, int reg, u16 val)
 	out_be16(host->sdhci.base + reg, val);
 }
 
+static u16 esdhc_op_read16_le_tuning(struct sdhci *sdhci, int reg)
+{
+	struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+	u16 ret = 0;
+	u32 val;
+
+	if (unlikely(reg == SDHCI_HOST_VERSION)) {
+		/*
+		 * The usdhc register returns a wrong host version.
+		 * Correct it here.
+		 */
+		return SDHCI_SPEC_300;
+	}
+
+	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
+		val = readl(sdhci->base + ESDHC_VENDOR_SPEC);
+		if (val & ESDHC_VENDOR_SPEC_VSELECT)
+			ret |= SDHCI_CTRL_VDD_180;
+
+		if (host->socdata->flags & ESDHC_FLAG_MAN_TUNING)
+			val = readl(sdhci->base + ESDHC_MIX_CTRL);
+		else if (host->socdata->flags & ESDHC_FLAG_STD_TUNING)
+			/* the std tuning bits is in ACMD12_ERR for imx6sl */
+			val = readl(sdhci->base + SDHCI_ACMD12_ERR__HOST_CONTROL2);
+
+		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
+			ret |= SDHCI_CTRL_EXEC_TUNING;
+		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
+			ret |= SDHCI_CTRL_TUNED_CLK;
+
+		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+
+		return ret;
+	}
+
+	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
+		u32 m = readl(sdhci->base + ESDHC_MIX_CTRL);
+		ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
+		/* Swap AC23 bit */
+		if (m & ESDHC_MIX_CTRL_AC23EN) {
+			ret &= ~ESDHC_MIX_CTRL_AC23EN;
+			ret |= SDHCI_TRNS_AUTO_CMD23;
+		}
+
+		return ret;
+	}
+
+	return readw(sdhci->base + reg);
+}
+
+static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci *sdhci)
+{
+	struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+	u32 present_state;
+	int ret;
+
+	ret = readl_poll_timeout(sdhci->base + ESDHC_PRSSTAT, present_state,
+				(present_state & ESDHC_CLOCK_GATE_OFF), 100);
+	if (ret == -ETIMEDOUT)
+		dev_warn(host->dev, "%s: card clock still not gate off in 100us!.\n", __func__);
+}
+
+static inline void esdhc_clrset_le(struct sdhci *sdhci, u32 mask, u32 val, int reg)
+{
+	void __iomem *base = sdhci->base + (reg & ~0x3);
+	u32 shift = (reg & 0x3) * 8;
+
+	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
+}
+
+static void esdhc_op_write16_le_tuning(struct sdhci *sdhci, int reg, u16 val)
+{
+	struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+	u32 new_val = 0;
+
+	switch (reg) {
+	case SDHCI_CLOCK_CONTROL:
+		new_val = readl(sdhci->base + ESDHC_VENDOR_SPEC);
+		if (val & SDHCI_CLOCK_CARD_EN)
+			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+		else
+			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+		writel(new_val, sdhci->base + ESDHC_VENDOR_SPEC);
+		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
+			esdhc_wait_for_card_clock_gate_off(sdhci);
+		return;
+	case SDHCI_HOST_CONTROL2:
+		new_val = readl(sdhci->base + ESDHC_VENDOR_SPEC);
+		if (val & SDHCI_CTRL_VDD_180)
+			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
+		else
+			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
+		writel(new_val, sdhci->base + ESDHC_VENDOR_SPEC);
+		if (host->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+			u32 v = readl(sdhci->base + SDHCI_ACMD12_ERR__HOST_CONTROL2);
+			u32 m = readl(sdhci->base + ESDHC_MIX_CTRL);
+			if (val & SDHCI_CTRL_TUNED_CLK) {
+				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+			} else {
+				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+			}
+
+			if (val & SDHCI_CTRL_EXEC_TUNING) {
+				v |= ESDHC_MIX_CTRL_EXE_TUNE;
+				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
+			} else {
+				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+			}
+
+			writel(v, sdhci->base + SDHCI_ACMD12_ERR__HOST_CONTROL2);
+			writel(m, sdhci->base + ESDHC_MIX_CTRL);
+		}
+		return;
+	case SDHCI_COMMAND:
+		if (host->last_cmd == MMC_CMD_STOP_TRANSMISSION)
+			val |= SDHCI_COMMAND_CMDTYP_ABORT;
+
+		writel(val << 16,
+		       sdhci->base + SDHCI_TRANSFER_MODE);
+		return;
+	case SDHCI_BLOCK_SIZE:
+		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+		break;
+	}
+	esdhc_clrset_le(sdhci, 0xffff, val, reg);
+}
+
+static u8 esdhc_readb_le(struct sdhci *sdhci, int reg)
+{
+	u8 ret;
+	u8 val;
+
+	switch (reg) {
+	case SDHCI_HOST_CONTROL:
+		val = readl(sdhci->base + reg);
+
+		ret = val & SDHCI_CTRL_LED;
+		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
+		ret |= (val & ESDHC_CTRL_4BITBUS);
+		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
+		return ret;
+	}
+
+	return readb(sdhci->base + reg);
+}
+
+static void esdhc_writeb_le(struct sdhci *sdhci, int reg, u8 val)
+{
+	u32 new_val = 0;
+	u32 mask;
+
+	switch (reg) {
+	case SDHCI_POWER_CONTROL:
+		/*
+		 * FSL put some DMA bits here
+		 * If your board has a regulator, code should be here
+		 */
+		return;
+	case SDHCI_HOST_CONTROL:
+		/* FSL messed up here, so we need to manually compose it. */
+		new_val = val & SDHCI_CTRL_LED;
+		/* ensure the endianness */
+		new_val |= ESDHC_HOST_CONTROL_LE;
+		/* DMA mode bits are shifted */
+		new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
+
+		/*
+		 * Do not touch buswidth bits here. This is done in
+		 * esdhc_pltfm_bus_width.
+		 * Do not touch the D3CD bit either which is used for the
+		 * SDIO interrupt erratum workaround.
+		 */
+		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
+
+		esdhc_clrset_le(sdhci, mask, new_val, reg);
+		return;
+	case SDHCI_SOFTWARE_RESET:
+		if (val & SDHCI_RESET_DATA)
+			new_val = readl(sdhci->base + SDHCI_HOST_CONTROL);
+		break;
+	}
+	esdhc_clrset_le(sdhci, 0xff, val, reg);
+
+	if (reg == SDHCI_SOFTWARE_RESET) {
+		if (val & SDHCI_RESET_ALL) {
+			/*
+			 * The esdhc has a design violation to SDHC spec which
+			 * tells that software reset should not affect card
+			 * detection circuit. But esdhc clears its SYSCTL
+			 * register bits [0..2] during the software reset. This
+			 * will stop those clocks that card detection circuit
+			 * relies on. To work around it, we turn the clocks on
+			 * back to keep card detection circuit functional.
+			 */
+			esdhc_clrset_le(sdhci, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
+			/*
+			 * The reset on usdhc fails to clear MIX_CTRL register.
+			 * Do it manually here.
+			 */
+			/*
+			 * the tuning bits should be kept during reset
+			 */
+			new_val = readl(sdhci->base + ESDHC_MIX_CTRL);
+			writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
+					sdhci->base + ESDHC_MIX_CTRL);
+		} else if (val & SDHCI_RESET_DATA) {
+			/*
+			 * The eSDHC DAT line software reset clears at least the
+			 * data transfer width on i.MX25, so make sure that the
+			 * Host Control register is unaffected.
+			 */
+			esdhc_clrset_le(sdhci, 0xff, new_val,
+					SDHCI_HOST_CONTROL);
+		}
+	}
+}
+
 void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
 {
 	if (host->socdata->flags & ESDHC_FLAG_BIGENDIAN) {
@@ -59,6 +281,11 @@ void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
 		host->sdhci.write16 = esdhc_op_write16_be;
 		host->sdhci.read32 = esdhc_op_read32_be;
 		host->sdhci.write32 = esdhc_op_write32_be;
+	} else if (esdhc_is_usdhc(host)){
+		host->sdhci.read8 = esdhc_readb_le;
+		host->sdhci.write8 = esdhc_writeb_le;
+		host->sdhci.read16 = esdhc_op_read16_le_tuning;
+		host->sdhci.write16 = esdhc_op_write16_le_tuning;
 	}
 }
 
@@ -100,6 +327,8 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 	dma_addr_t dma = SDHCI_NO_DMA;
 	int ret;
 
+	host->last_cmd = cmd ? cmd->cmdidx : 0;
+
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, -1);
 
 	/* Wait at least 8 SD clock cycles before the next command */
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index e24d76d0c687..df8aca18ad32 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -66,6 +66,36 @@
 #define  IMX_SDHCI_DLL_CTRL_OVERRIDE_EN_SHIFT	8
 #define IMX_SDHCI_MIX_CTRL_FBCLK_SEL	BIT(25)
 
+/* pltfm-specific */
+#define ESDHC_HOST_CONTROL_LE	0x20
+
+/*
+ * eSDHC register definition
+ */
+
+/* Present State Register */
+#define ESDHC_PRSSTAT			0x24
+#define ESDHC_CLOCK_GATE_OFF		0x00000080
+#define ESDHC_CLOCK_STABLE		0x00000008
+
+/* Protocol Control Register */
+#define ESDHC_PROCTL			0x28
+#define ESDHC_VOLT_SEL			0x00000400
+#define ESDHC_CTRL_4BITBUS		(0x1 << 1)
+#define ESDHC_CTRL_8BITBUS		(0x2 << 1)
+#define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
+#define ESDHC_HOST_CONTROL_RES		0x01
+
+/* System Control Register */
+#define ESDHC_SYSTEM_CONTROL		0x2c
+#define ESDHC_CLOCK_MASK		0x0000fff0
+#define ESDHC_PREDIV_SHIFT		8
+#define ESDHC_DIVIDER_SHIFT		4
+#define ESDHC_CLOCK_SDCLKEN		0x00000008
+#define ESDHC_CLOCK_PEREN		0x00000004
+#define ESDHC_CLOCK_HCKEN		0x00000002
+#define ESDHC_CLOCK_IPGEN		0x00000001
+
 /* tune control register */
 #define ESDHC_TUNE_CTRL_STATUS		0x68
 #define  ESDHC_TUNE_CTRL_STEP		1
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index e0436425fbda..038b52cfe619 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -143,6 +143,7 @@
 #define  SDHCI_CTRL_TUNED_CLK			BIT(7)
 #define  SDHCI_CTRL_64BIT_ADDR			BIT(13)
 #define  SDHCI_CTRL_V4_MODE			BIT(12)
+#define  SDHCI_CTRL_PRESET_VAL_ENABLE		BIT(15)
 #define SDHCI_CAPABILITIES					0x40
 #define  SDHCI_TIMEOUT_CLK_MASK			GENMASK(5, 0)
 #define  SDHCI_TIMEOUT_CLK_UNIT			0x00000080
-- 
2.39.5




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

* [PATCH v2 29/30] mci: sdhci: support Linux SDHCI_QUIRK2_BROKEN_HS200 flag
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (27 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 28/30] mci: imx-esdhc: fixup quirks in standard SDHCI registers Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-07  8:22 ` [PATCH v2 30/30] mci: imx-esdhc: implement HS200 support Ahmad Fatoum
  2025-05-08  7:30 ` [PATCH v2 00/30] mci: imx-esdhc: add " Sascha Hauer
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

In Linux HS200 is opt-out for SDHCI. Let's do the same in barebox.
We intentionally keep MMC_CAP2_HS200 for Arasan, as I have not verified
on the hardware that the SDHCI implementation there correctly reports
SDR104 as supported (which would imply HS200 when used for eMMC).

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/am654-sdhci.c            |  3 +++
 drivers/mci/arasan-sdhci.c           |  3 +++
 drivers/mci/dwcmshc-sdhci.c          |  2 ++
 drivers/mci/imx-esdhc.c              |  3 +++
 drivers/mci/rockchip-dwcmshc-sdhci.c |  3 +++
 drivers/mci/sdhci.c                  | 32 ++++++++++++++++++++++++++++
 drivers/mci/sdhci.h                  |  4 ++++
 7 files changed, 50 insertions(+)

diff --git a/drivers/mci/am654-sdhci.c b/drivers/mci/am654-sdhci.c
index 13c8876573c7..be0a6e09f796 100644
--- a/drivers/mci/am654-sdhci.c
+++ b/drivers/mci/am654-sdhci.c
@@ -638,6 +638,9 @@ static int am654_sdhci_probe(struct device *dev)
 	if (ret)
 		return ret;
 
+	/* HS200 not supported by this driver at the moment */
+	plat->sdhci.quirks2 = SDHCI_QUIRK2_BROKEN_HS200;
+
 	plat->sdhci.mci = mci;
 	sdhci_setup_host(&plat->sdhci);
 
diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index ceef0b63a8b9..4adaaa243d2c 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -773,6 +773,9 @@ static int arasan_sdhci_probe(struct device *dev)
 			mci->ops.execute_tuning = arasan_zynqmp_execute_tuning;
 		mci->caps2 |= MMC_CAP2_HS200;
 		arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN;
+	} else {
+		/* HS200 only supported for ZynqMP at the moment */
+		arasan_sdhci->sdhci.quirks2 = SDHCI_QUIRK2_BROKEN_HS200;
 	}
 
 	/*
diff --git a/drivers/mci/dwcmshc-sdhci.c b/drivers/mci/dwcmshc-sdhci.c
index 174cc3f76816..6fdbb61565f2 100644
--- a/drivers/mci/dwcmshc-sdhci.c
+++ b/drivers/mci/dwcmshc-sdhci.c
@@ -325,6 +325,8 @@ static int dwcmshc_probe(struct device *dev)
 	host->sdhci.base = IOMEM(iores->start);
 	host->sdhci.mci = mci;
 	host->sdhci.max_clk = clk_get_rate(clk);
+	/* HS200 not supported by this driver at the moment */
+	host->sdhci.quirks2 = SDHCI_QUIRK2_BROKEN_HS200;
 	host->cb = dwcmshc_cb;
 
 	mci->hw_dev = dev;
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 00b7e0693fcc..4f2f02cfbdbe 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -466,6 +466,9 @@ static int fsl_esdhc_probe(struct device *dev)
 	host->mci.hw_dev = dev;
 	host->sdhci.mci = &host->mci;
 
+	if (!(host->socdata->flags & ESDHC_FLAG_HS200))
+		host->sdhci.quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
+
 	ret = sdhci_setup_host(&host->sdhci);
 	if (ret)
 		goto err_clk_disable;
diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c
index 2f8d5ec140b5..c4c03f703a15 100644
--- a/drivers/mci/rockchip-dwcmshc-sdhci.c
+++ b/drivers/mci/rockchip-dwcmshc-sdhci.c
@@ -341,6 +341,9 @@ static int rk_sdhci_probe(struct device *dev)
 
 	mci_of_parse(&host->mci);
 
+	/* HS200 not supported by this driver at the moment */
+	host->sdhci.quirks2 = SDHCI_QUIRK2_BROKEN_HS200;
+
 	sdhci_setup_host(&host->sdhci);
 
 	dev->priv = host;
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index c8fd78a5e62d..17847a13ed5f 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -1044,6 +1044,38 @@ int sdhci_setup_host(struct sdhci *host)
 	if (sdhci_can_64bit_dma(host))
 		host->flags |= SDHCI_USE_64_BIT_DMA;
 
+	if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
+		host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
+				 SDHCI_SUPPORT_DDR50);
+		/*
+		 * The SDHCI controller in a SoC might support HS200/HS400
+		 * (indicated using mmc-hs200-1_8v/mmc-hs400-1_8v dt property),
+		 * but if the board is modeled such that the IO lines are not
+		 * connected to 1.8v then HS200/HS400 cannot be supported.
+		 * Disable HS200/HS400 if the board does not have 1.8v connected
+		 * to the IO lines. (Applicable for other modes in 1.8v)
+		 */
+		mci->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES);
+		mci->host_caps &= ~(MMC_CAP_1_8V_DDR | MMC_CAP_UHS);
+	}
+
+	/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
+	if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
+			   SDHCI_SUPPORT_DDR50))
+		mci->host_caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+
+	/* SDR104 supports also implies SDR50 support */
+	if (host->caps1 & SDHCI_SUPPORT_SDR104) {
+		mci->host_caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
+		/* SD3.0: SDR104 is supported so (for eMMC) the caps2
+		 * field can be promoted to support HS200.
+		 */
+		if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
+			mci->caps2 |= MMC_CAP2_HS200;
+	} else if (host->caps1 & SDHCI_SUPPORT_SDR50) {
+		mci->host_caps |= MMC_CAP_UHS_SDR50;
+	}
+
 	if ((mci->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
 		host->flags |= SDHCI_SIGNALING_180;
 
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 038b52cfe619..25d257b23145 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -249,6 +249,10 @@ struct sdhci {
 	unsigned int quirks;
 #define SDHCI_QUIRK_MISSING_CAPS		BIT(27)
 	unsigned int quirks2;
+/* The system physically doesn't support 1.8v, even if the host does */
+#define SDHCI_QUIRK2_NO_1_8_V				BIT(2)
+/* Controller does not support HS200 */
+#define SDHCI_QUIRK2_BROKEN_HS200			BIT(6)
 #define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN	BIT(15)
 #define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER	BIT(19)
 	u32 caps;	/* CAPABILITY_0 */
-- 
2.39.5




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

* [PATCH v2 30/30] mci: imx-esdhc: implement HS200 support
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (28 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 29/30] mci: sdhci: support Linux SDHCI_QUIRK2_BROKEN_HS200 flag Ahmad Fatoum
@ 2025-05-07  8:22 ` Ahmad Fatoum
  2025-05-08  7:30 ` [PATCH v2 00/30] mci: imx-esdhc: add " Sascha Hauer
  30 siblings, 0 replies; 32+ messages in thread
From: Ahmad Fatoum @ 2025-05-07  8:22 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Now everything is in place, so we can actually implement execute_tuning
for i.MX6SL or later. i.MX6Q would be possible as well, but that
requires a different manual tuning scheme which was not ported here.

Comparison copying 64M off a Samsung 8GTF4R eMMC on an i.MX8MP.
Before:

  imx8mp-ddr52: time cp /dev/mmc2.2 .
  time: 884ms

After:

  imx8mp-hs200: time cp /dev/mmc2.2 .
  time: 488ms

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc-common.c |   8 +-
 drivers/mci/imx-esdhc.c        | 153 ++++++++++++++++++++++++++++++++-
 2 files changed, 157 insertions(+), 4 deletions(-)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index caa8505ac00e..228f4a9b03fe 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -323,7 +323,7 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 		     struct mci_data *data)
 {
 	u32	xfertyp, mixctrl, command;
-	u32	val, irqstat;
+	u32	val, irqflags, irqstat;
 	dma_addr_t dma = SDHCI_NO_DMA;
 	int ret;
 
@@ -363,9 +363,13 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 	sdhci_write32(&host->sdhci, SDHCI_TRANSFER_MODE__COMMAND,
 		      command << 16 | xfertyp);
 
+	irqflags = SDHCI_INT_CMD_COMPLETE;
+	if (mmc_op_tuning(cmd->cmdidx))
+		irqflags = SDHCI_INT_DATA_AVAIL;
+
 	/* Wait for the command to complete */
 	ret = esdhc_poll(host, SDHCI_INT_STATUS, irqstat,
-			 irqstat & SDHCI_INT_CMD_COMPLETE,
+			 irqstat & irqflags,
 			 100 * MSECOND);
 	if (ret) {
 		dev_dbg(host->dev, "timeout 1\n");
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index 4f2f02cfbdbe..c6547d94a4f9 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -61,6 +61,14 @@
 #define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
 #define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
 
+/*
+ * Our interpretation of the SDHCI_HOST_CONTROL register
+ */
+#define ESDHC_CTRL_4BITBUS		(0x1 << 1)
+#define ESDHC_CTRL_8BITBUS		(0x2 << 1)
+#define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
+#define USDHC_GET_BUSWIDTH(c) (c & ESDHC_CTRL_BUSWIDTH_MASK)
+
 #define to_fsl_esdhc(mci)	container_of(mci, struct fsl_esdhc_host, mci)
 
 /*
@@ -137,6 +145,104 @@ static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
 		   10 * MSECOND);
 }
 
+static inline void esdhc_clrset_le(struct fsl_esdhc_host *host,
+				   u32 mask, u32 val, int reg)
+{
+	void __iomem *base = host->sdhci.base + (reg & ~0x3);
+	u32 shift = (reg & 0x3) * 8;
+
+	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
+}
+
+/* Enable the auto tuning circuit to check the CMD line and BUS line */
+static inline void usdhc_auto_tuning_mode_sel_and_en(struct fsl_esdhc_host *host)
+{
+	u32 buswidth, auto_tune_buswidth;
+	u32 reg;
+
+	buswidth = USDHC_GET_BUSWIDTH(sdhci_read32(&host->sdhci, SDHCI_HOST_CONTROL));
+
+	switch (buswidth) {
+	case ESDHC_CTRL_8BITBUS:
+		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN;
+		break;
+	case ESDHC_CTRL_4BITBUS:
+		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN;
+		break;
+	default:	/* 1BITBUS */
+		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
+		break;
+	}
+
+	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
+			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
+			ESDHC_VEND_SPEC2);
+
+	reg = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
+	reg |= MIX_CTRL_AUTO_TUNE_EN;
+	sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, reg);
+}
+
+static void esdhc_reset_tuning(struct fsl_esdhc_host *host)
+{
+	u32 ctrl;
+	int ret;
+
+	/* Reset the tuning circuit */
+	if (esdhc_is_usdhc(host)) {
+		ctrl = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
+		ctrl &= ~MIX_CTRL_AUTO_TUNE_EN;
+		if (host->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+			sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, ctrl);
+			ctrl = sdhci_read32(&host->sdhci, SDHCI_ACMD12_ERR__HOST_CONTROL2);
+			ctrl &= ~MIX_CTRL_SMPCLK_SEL;
+			ctrl &= ~MIX_CTRL_EXE_TUNE;
+			sdhci_write32(&host->sdhci, SDHCI_ACMD12_ERR__HOST_CONTROL2, ctrl);
+			/* Make sure MIX_CTRL_EXE_TUNE cleared */
+			ret = sdhci_read32_poll_timeout(&host->sdhci, SDHCI_ACMD12_ERR__HOST_CONTROL2,
+				ctrl, !(ctrl & MIX_CTRL_EXE_TUNE), 50);
+			if (ret == -ETIMEDOUT)
+				dev_warn(host->dev,
+				 "Warning! clear execute tuning bit failed\n");
+			/*
+			 * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
+			 * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
+			 * will finally make sure the normal data transfer logic correct.
+			 */
+			ctrl = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
+			ctrl |= SDHCI_INT_DATA_AVAIL;
+			sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ctrl);
+		}
+	}
+}
+
+static int usdhc_execute_tuning(struct mci_host *mci, u32 opcode)
+{
+	struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
+	int err;
+
+	/*
+	 * i.MX uSDHC internally already uses a fixed optimized timing for
+	 * DDR50, normally does not require tuning for DDR50 mode.
+	 */
+	if (host->sdhci.timing == MMC_TIMING_UHS_DDR50)
+		return 0;
+
+	/*
+	 * Reset tuning circuit logic. If not, the previous tuning result
+	 * will impact current tuning, make current tuning can't set the
+	 * correct delay cell.
+	 */
+	esdhc_reset_tuning(host);
+
+	err = sdhci_execute_tuning(&host->sdhci, opcode);
+	/* If tuning done, enable auto tuning */
+	if (!err)
+		usdhc_auto_tuning_mode_sel_and_en(host);
+
+	return err;
+}
+
 static int esdhc_change_pinstate(struct fsl_esdhc_host *host,
 				 unsigned int uhs)
 {
@@ -190,8 +296,18 @@ static void usdhc_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing
 			sdhci_write32(&host->sdhci, IMX_SDHCI_DLL_CTRL, v);
 		}
 		break;
-	default:
+	case MMC_TIMING_UHS_SDR12:
+	case MMC_TIMING_UHS_SDR25:
+	case MMC_TIMING_UHS_SDR50:
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_MMC_HS:
+	case MMC_TIMING_MMC_HS200:
 		sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+		break;
+	case MMC_TIMING_LEGACY:
+	default:
+		esdhc_reset_tuning(host);
+		break;
 	}
 
 	esdhc_change_pinstate(host, timing);
@@ -421,6 +537,39 @@ static void fsl_esdhc_probe_dt(struct device *dev, struct fsl_esdhc_host *host)
 	}
 }
 
+static bool usdhc_setup_tuning(struct fsl_esdhc_host *host)
+{
+	struct mci_host *mci = &host->mci;
+
+	if (!IS_ENABLED(CONFIG_MCI_TUNING) || !esdhc_is_usdhc(host))
+		return false;
+
+	/*
+	 * clear tuning bits in case ROM has set it already
+	 * We use writel, because we haven't set up SDHCI yet
+	 */
+	writel(0x0, host->sdhci.base + IMX_SDHCI_MIXCTRL);
+	writel(0x0, host->sdhci.base + SDHCI_ACMD12_ERR__HOST_CONTROL2);
+	writel(0x0, host->sdhci.base + ESDHC_TUNE_CTRL_STATUS);
+
+	if (!(host->socdata->flags & ESDHC_FLAG_HS200))
+		return false;
+
+	if (host->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
+		dev_dbg(host->dev, "i.MX6Q manual tuning not supported\n");
+		return false;
+	}
+
+	/*
+	 * Link usdhc specific mmc_host_ops execute_tuning function,
+	 * to replace the standard one in sdhci_ops.
+	 */
+	mci->ops.execute_tuning = usdhc_execute_tuning;
+	mci->caps2 |= MMC_CAP2_HS200;
+
+	return true;
+}
+
 static int fsl_esdhc_probe(struct device *dev)
 {
 	struct resource *iores;
@@ -466,7 +615,7 @@ static int fsl_esdhc_probe(struct device *dev)
 	host->mci.hw_dev = dev;
 	host->sdhci.mci = &host->mci;
 
-	if (!(host->socdata->flags & ESDHC_FLAG_HS200))
+	if (!usdhc_setup_tuning(host))
 		host->sdhci.quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
 
 	ret = sdhci_setup_host(&host->sdhci);
-- 
2.39.5




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

* Re: [PATCH v2 00/30] mci: imx-esdhc: add HS200 support
  2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
                   ` (29 preceding siblings ...)
  2025-05-07  8:22 ` [PATCH v2 30/30] mci: imx-esdhc: implement HS200 support Ahmad Fatoum
@ 2025-05-08  7:30 ` Sascha Hauer
  30 siblings, 0 replies; 32+ messages in thread
From: Sascha Hauer @ 2025-05-08  7:30 UTC (permalink / raw)
  To: barebox, Ahmad Fatoum


On Wed, 07 May 2025 10:21:39 +0200, Ahmad Fatoum wrote:
> On my i.MX8MP board this increases sequential read nearly from
> 72 MiB/s to 131 MiB/s.
> 
> I also got HS400ES running, but it gives me occasional CRC errors, so
> this series only concerns itself with HS200 for now.
> 
> v1 -> v2:
>   - use correct speed comparison in commit message. v1 had KASAN
>   and DMA_API_DEBUG enabled..
>   - define and use new sdhci_compute_timeout helper (Sascha)
>   - remove superfluous comment about clearing bits (Sascha)
>   - always populate delay settings in boarddata (Sascha)
>   - reduce number of Linux SDHCI quirks mentioned in commit
>     message to 51 (Sascha)
> 
> [...]

Applied, thanks!

[01/30] mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition
        https://git.pengutronix.de/cgit/barebox/commit/?id=3854b61a6eb2 (link may not be stable)
[02/30] mci: move most recent I/O settings into mci_host::ios
        https://git.pengutronix.de/cgit/barebox/commit/?id=365b66461683 (link may not be stable)
[03/30] mci: use struct mci_host::ios inside mci_set_ios
        https://git.pengutronix.de/cgit/barebox/commit/?id=103d060182d0 (link may not be stable)
[04/30] mci: tuning: fix fallback to DDR52
        https://git.pengutronix.de/cgit/barebox/commit/?id=3cf0ddbd3f19 (link may not be stable)
[05/30] mci: sdhci: unmap DMA buffers on timeout
        https://git.pengutronix.de/cgit/barebox/commit/?id=ce6ff8b4be4d (link may not be stable)
[06/30] mci: add MMC_CAP_UHS constants
        https://git.pengutronix.de/cgit/barebox/commit/?id=88f55d158d33 (link may not be stable)
[07/30] mci: rename MMC_CAP_MMC_x_yV_DDR to MMC_CAP_x_yV_DDR as in Linux
        https://git.pengutronix.de/cgit/barebox/commit/?id=b8b702948038 (link may not be stable)
[08/30] mci: compare host and card caps for supported speeds
        https://git.pengutronix.de/cgit/barebox/commit/?id=a242a89a274f (link may not be stable)
[09/30] mci: print HS200 capabilities in devinfo
        https://git.pengutronix.de/cgit/barebox/commit/?id=d3882ad438fc (link may not be stable)
[10/30] mci: respect no-1-8-v OF property
        https://git.pengutronix.de/cgit/barebox/commit/?id=50c62ba74801 (link may not be stable)
[11/30] mci: sdhci: add support for struct mci_data::timeout_ns
        https://git.pengutronix.de/cgit/barebox/commit/?id=bbecd0b7bb7e (link may not be stable)
[12/30] mci: imx-esdhc: use unsigned types where appropriate
        https://git.pengutronix.de/cgit/barebox/commit/?id=e535fc5d3e83 (link may not be stable)
[13/30] mci: imx-esdhc: implement esdhc_poll using sdhci_read32_poll_timeout
        https://git.pengutronix.de/cgit/barebox/commit/?id=02986964885c (link may not be stable)
[14/30] mci: imx-esdhc: drop one extra read of SDHCI_INT_STATUS
        https://git.pengutronix.de/cgit/barebox/commit/?id=a1aa6df5f81f (link may not be stable)
[15/30] mci: sdhci: add cmd parameter to sdhci_transfer_*
        https://git.pengutronix.de/cgit/barebox/commit/?id=f35385a1ccaa (link may not be stable)
[16/30] mci: arasan: introduce mmc_op_tuning helper
        https://git.pengutronix.de/cgit/barebox/commit/?id=60c69c089fee (link may not be stable)
[17/30] mci: imx-esdhc: flesh out register description
        https://git.pengutronix.de/cgit/barebox/commit/?id=a92af4b8188b (link may not be stable)
[18/30] mci: imx-esdhc: add support for delay/tuning properties in DT
        https://git.pengutronix.de/cgit/barebox/commit/?id=fadd0942f11d (link may not be stable)
[19/30] mci: add mci_set_timing helper
        https://git.pengutronix.de/cgit/barebox/commit/?id=663eb4f5b8df (link may not be stable)
[20/30] mci: imx-esdhc: add support for setting drive strength
        https://git.pengutronix.de/cgit/barebox/commit/?id=ab10a18526e1 (link may not be stable)
[21/30] mci: sdhci: move SDHCI_MAKE_BLKSZ definition to header
        https://git.pengutronix.de/cgit/barebox/commit/?id=f37f0ab77c79 (link may not be stable)
[22/30] mci: imx-esdhc: select different pinctrl state depending on frequency
        https://git.pengutronix.de/cgit/barebox/commit/?id=8395625eec32 (link may not be stable)
[23/30] mci: core: retry MMC_CMD_SET_BLOCKLEN up to 4 times
        https://git.pengutronix.de/cgit/barebox/commit/?id=3f1a9f9037e4 (link may not be stable)
[24/30] mci: imx-esdhc: don't reconfigure clock unless required
        https://git.pengutronix.de/cgit/barebox/commit/?id=fe803b9faaf8 (link may not be stable)
[25/30] mci: sdhci: fix sdhci_transfer_data MMC_SEND_TUNING compatibility
        https://git.pengutronix.de/cgit/barebox/commit/?id=12d4384a64cc (link may not be stable)
[26/30] mci: core: implement mmc_send_tuning
        https://git.pengutronix.de/cgit/barebox/commit/?id=8adfce51b38d (link may not be stable)
[27/30] mci: imx-esdhc: set burst_length_enable
        https://git.pengutronix.de/cgit/barebox/commit/?id=30a7d2c657fa (link may not be stable)
[28/30] mci: imx-esdhc: fixup quirks in standard SDHCI registers
        https://git.pengutronix.de/cgit/barebox/commit/?id=cb831446eb5f (link may not be stable)
[29/30] mci: sdhci: support Linux SDHCI_QUIRK2_BROKEN_HS200 flag
        https://git.pengutronix.de/cgit/barebox/commit/?id=72a71ebb5f91 (link may not be stable)
[30/30] mci: imx-esdhc: implement HS200 support
        https://git.pengutronix.de/cgit/barebox/commit/?id=bfcae5ddd3d3 (link may not be stable)

Best regards,
-- 
Sascha Hauer <s.hauer@pengutronix.de>




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

end of thread, other threads:[~2025-05-08  7:35 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 01/30] mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 02/30] mci: move most recent I/O settings into mci_host::ios Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 03/30] mci: use struct mci_host::ios inside mci_set_ios Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 04/30] mci: tuning: fix fallback to DDR52 Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 05/30] mci: sdhci: unmap DMA buffers on timeout Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 06/30] mci: add MMC_CAP_UHS constants Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 07/30] mci: rename MMC_CAP_MMC_x_yV_DDR to MMC_CAP_x_yV_DDR as in Linux Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 08/30] mci: compare host and card caps for supported speeds Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 09/30] mci: print HS200 capabilities in devinfo Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 10/30] mci: respect no-1-8-v OF property Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 11/30] mci: sdhci: add support for struct mci_data::timeout_ns Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 12/30] mci: imx-esdhc: use unsigned types where appropriate Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 13/30] mci: imx-esdhc: implement esdhc_poll using sdhci_read32_poll_timeout Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 14/30] mci: imx-esdhc: drop one extra read of SDHCI_INT_STATUS Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 15/30] mci: sdhci: add cmd parameter to sdhci_transfer_* Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 16/30] mci: arasan: introduce mmc_op_tuning helper Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 17/30] mci: imx-esdhc: flesh out register description Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 18/30] mci: imx-esdhc: add support for delay/tuning properties in DT Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 19/30] mci: add mci_set_timing helper Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 20/30] mci: imx-esdhc: add support for setting drive strength Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 21/30] mci: sdhci: move SDHCI_MAKE_BLKSZ definition to header Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 22/30] mci: imx-esdhc: select different pinctrl state depending on frequency Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 23/30] mci: core: retry MMC_CMD_SET_BLOCKLEN up to 4 times Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 24/30] mci: imx-esdhc: don't reconfigure clock unless required Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 25/30] mci: sdhci: fix sdhci_transfer_data MMC_SEND_TUNING compatibility Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 26/30] mci: core: implement mmc_send_tuning Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 27/30] mci: imx-esdhc: set burst_length_enable Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 28/30] mci: imx-esdhc: fixup quirks in standard SDHCI registers Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 29/30] mci: sdhci: support Linux SDHCI_QUIRK2_BROKEN_HS200 flag Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 30/30] mci: imx-esdhc: implement HS200 support Ahmad Fatoum
2025-05-08  7:30 ` [PATCH v2 00/30] mci: imx-esdhc: add " Sascha Hauer

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