mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: BAREBOX <barebox@lists.infradead.org>
Cc: "Claude Opus 4.7" <noreply@anthropic.com>
Subject: [PATCH 04/10] mci: add HS400 mode selection
Date: Mon, 11 May 2026 14:07:59 +0200	[thread overview]
Message-ID: <20260511-rockchip-emmc-hs400-v1-4-515fb6d20e12@pengutronix.de> (raw)
In-Reply-To: <20260511-rockchip-emmc-hs400-v1-0-515fb6d20e12@pengutronix.de>

eMMC HS400 transitions through HS and 8-bit DDR per JEDEC, on top of
a card already tuned in HS200. Wire that up in the core:

- mmc_select_max_dtr() now records EXT_CSD_CARD_TYPE_HS400_{1_8V,1_2V}
  in mmc_avail_type when both the host (mmc-hs400-1_8v / mmc-hs400-1_2v
  in DT) and the card advertise it. HS400 reuses hs200_max_dtr (200 MHz)
  since it's the same SDCLK rate, just DDR-sampled.

- mmc_set_bus_speed() treats HS200 and HS400 as the same rate tier.

- mmc_select_hs400() implements the JEDEC sequence:
    1. CMD6 EXT_CSD_HS_TIMING = HS
    2. host: MMC_TIMING_MMC_HS, clock = hs_max_dtr
    3. CMD6 EXT_CSD_BUS_WIDTH = EXT_CSD_DDR_BUS_WIDTH_8
    4. CMD6 EXT_CSD_HS_TIMING = HS400
    5. host: MMC_TIMING_MMC_HS400, clock back to 200 MHz

  Bails early if the card lacks HS400 capability or the bus isn't 8-bit.

- mci_startup_mmc() runs mmc_select_hs400() right after a successful
  HS200 tuning when EXT_CSD_CARD_TYPE_HS400 is in mmc_avail_type.

- mmc_card_hs400() helper added in mci.h, mirroring mmc_card_hs200().

Host drivers must additionally handle MMC_TIMING_MMC_HS400 in their
set_ios / set_clock paths (e.g. setting the controller-specific HS400
bit in HOST_CONTROL2, configuring data-strobe sampling). Without that
the core will run the transition but the host won't sample correctly.

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mci/mci-core.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++--
 include/mci.h          |  5 ++++
 2 files changed, 82 insertions(+), 2 deletions(-)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 23b9117869..58b28ec653 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -126,7 +126,8 @@ static int mci_set_blocklen(struct mci *mci, unsigned len)
 {
 	struct mci_cmd cmd = {0};
 
-	if (mci->host->ios.timing == MMC_TIMING_MMC_DDR52)
+	if (mci->host->ios.timing == MMC_TIMING_MMC_DDR52 ||
+	    mmc_card_hs400(mci))
 		return 0;
 
 	mci_setup_cmd(&cmd, MMC_CMD_SET_BLOCKLEN, len, MMC_RSP_R1);
@@ -1051,6 +1052,8 @@ static const char *mci_timing_tostr(unsigned timing)
 		return "MMC DDR52";
 	case MMC_TIMING_MMC_HS200:
 		return "HS200";
+	case MMC_TIMING_MMC_HS400:
+		return "HS400";
 	default:
 		return "unknown"; /* shouldn't happen */
 	}
@@ -1783,6 +1786,18 @@ static void mmc_select_max_dtr(struct mci *mci)
 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V;
 	}
 
+	if ((caps2 & MMC_CAP2_HS400_1_8V) &&
+	    (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) {
+		hs200_max_dtr = MMC_HS200_MAX_DTR;
+		avail_type |= EXT_CSD_CARD_TYPE_HS400_1_8V;
+	}
+
+	if ((caps2 & MMC_CAP2_HS400_1_2V) &&
+	    (card_type & EXT_CSD_CARD_TYPE_HS400_1_2V)) {
+		hs200_max_dtr = MMC_HS200_MAX_DTR;
+		avail_type |= EXT_CSD_CARD_TYPE_HS400_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;
@@ -1875,7 +1890,7 @@ static void mmc_set_bus_speed(struct mci *mci)
 {
 	unsigned int max_dtr = (unsigned int)-1;
 
-	if (mmc_card_hs200(mci) &&
+	if ((mmc_card_hs200(mci) || mmc_card_hs400(mci)) &&
 		max_dtr > mci->host->hs200_max_dtr)
 		max_dtr = mci->host->hs200_max_dtr;
 	else if (mmc_card_hs(mci) && max_dtr > mci->host->hs_max_dtr)
@@ -1921,6 +1936,62 @@ int mmc_hs200_tuning(struct mci *mci)
 	return mci_execute_tuning(mci);
 }
 
+/*
+ * Switch from HS200 to HS400 per JEDEC. The card must already be in HS200
+ * (with successful tuning) and 8-bit bus width. The transition sequence is:
+ *
+ *   1. Switch the card back to HS timing.
+ *   2. Drop the host clock to HS rate (<= 52 MHz).
+ *   3. Switch the card to 8-bit DDR bus width.
+ *   4. Switch the card to HS400 timing.
+ *   5. Switch the host to HS400 timing and ramp the clock back to HS200 rate
+ *      (200 MHz, used as DDR so effective 400 MHz / 8-bit data lane).
+ */
+static int mmc_select_hs400(struct mci *mci)
+{
+	struct mci_host *host = mci->host;
+	int err;
+	u8 val;
+
+	if (!(host->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400) ||
+	    host->ios.bus_width != MMC_BUS_WIDTH_8)
+		return 0;
+
+	/* Step 1: switch card back to HS timing */
+	err = mci_switch(mci, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
+	if (err) {
+		dev_err(&mci->dev, "switch to HS from HS200 failed: %d\n", err);
+		return err;
+	}
+
+	/* Step 2: drop host clock to HS rate */
+	mci_set_timing(mci, MMC_TIMING_MMC_HS);
+	mci_set_clock(mci, host->hs_max_dtr);
+
+	/* Step 3: switch card to 8-bit DDR */
+	err = mci_switch(mci, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8);
+	if (err) {
+		dev_err(&mci->dev, "switch to DDR for HS400 failed: %d\n", err);
+		return err;
+	}
+
+	/* Step 4: switch card to HS400 timing */
+	val = EXT_CSD_TIMING_HS400 | (host->drive_strength << EXT_CSD_DRV_STR_SHIFT);
+	err = mci_switch(mci, EXT_CSD_HS_TIMING, val);
+	if (err) {
+		dev_err(&mci->dev, "switch to HS400 failed: %d\n", err);
+		return err;
+	}
+
+	/* Step 5: switch host to HS400 timing and ramp clock */
+	mci_set_timing(mci, MMC_TIMING_MMC_HS400);
+	mmc_set_bus_speed(mci);
+
+	dev_dbg(&mci->dev, "HS400 selected\n");
+
+	return 0;
+}
+
 static int mci_startup_mmc(struct mci *mci)
 {
 	struct mci_host *host = mci->host;
@@ -1947,6 +2018,10 @@ static int mci_startup_mmc(struct mci *mci)
 			ret = mmc_hs200_tuning(mci);
 			if (!ret) {
 				dev_dbg(&mci->dev, "HS200 tuning succeeded\n");
+
+				if (host->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400)
+					mmc_select_hs400(mci);
+
 				return 0;
 			}
 
diff --git a/include/mci.h b/include/mci.h
index 8348f97432..a7cd0c5ae2 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -802,4 +802,9 @@ static inline bool mmc_card_hs200(struct mci *mci)
 	return mci->host->ios.timing == MMC_TIMING_MMC_HS200;
 }
 
+static inline bool mmc_card_hs400(struct mci *mci)
+{
+	return mci->host->ios.timing == MMC_TIMING_MMC_HS400;
+}
+
 #endif /* _MCI_H_ */

-- 
2.47.3




  parent reply	other threads:[~2026-05-11 12:10 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-11 12:07 [PATCH 00/10] mci: rockchip-dwcmshc: add HS400(ES) support Sascha Hauer
2026-05-11 12:07 ` [PATCH 01/10] mci: sdhci: define VDD_180 and shrink UHS_MASK to bits 0..2 Sascha Hauer
2026-05-18  8:58   ` Ahmad Fatoum
2026-05-11 12:07 ` [PATCH 02/10] mci: mmc_send_tuning: actually point data.dest at the buffer Sascha Hauer
2026-05-11 12:49   ` Ahmad Fatoum
2026-05-11 12:07 ` [PATCH 03/10] mci: sdhci: add ADMA2 descriptor helpers Sascha Hauer
2026-05-18  9:18   ` Ahmad Fatoum
2026-05-18 12:16     ` Sascha Hauer
2026-05-18 12:20       ` Ahmad Fatoum
2026-05-11 12:07 ` Sascha Hauer [this message]
2026-05-18  9:36   ` [PATCH 04/10] mci: add HS400 mode selection Ahmad Fatoum
2026-05-18 12:35     ` Sascha Hauer
2026-05-11 12:08 ` [PATCH 05/10] mci: add HS400 Enhanced Strobe (HS400ES) selection Sascha Hauer
2026-05-18  9:54   ` Ahmad Fatoum
2026-05-18 13:06     ` Sascha Hauer
2026-05-11 12:08 ` [PATCH 06/10] mci: rockchip-dwcmshc-sdhci: use ADMA2 Sascha Hauer
2026-05-11 12:55   ` Ahmad Fatoum
2026-05-11 14:01     ` Sascha Hauer
2026-05-11 14:06       ` Ahmad Fatoum
2026-05-11 12:08 ` [PATCH 07/10] mci: sdhci: rockchip: set TX-path source-select bit in DWCMSHC_EMMC_DLL_TXCLK Sascha Hauer
2026-05-18  9:57   ` Ahmad Fatoum
2026-05-11 12:08 ` [PATCH 08/10] mci: sdhci: rockchip: distinguish IP revision 0 (rk3568) from 1 (rk3576/rk3588) Sascha Hauer
2026-05-18  9:59   ` Ahmad Fatoum
2026-05-11 12:08 ` [PATCH 09/10] mci: sdhci: rockchip: support HS400 Sascha Hauer
2026-05-18 10:09   ` Ahmad Fatoum
2026-05-11 12:08 ` [PATCH 10/10] mci: sdhci: rockchip: support HS400 Enhanced Strobe Sascha Hauer
2026-05-18 10:10   ` Ahmad Fatoum

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20260511-rockchip-emmc-hs400-v1-4-515fb6d20e12@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=noreply@anthropic.com \
    /path/to/YOUR_REPLY

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

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