[PATCH v18 12/12] mmc: renesas_sdhi: Add HS400 enhanced strobe support for RZ/G3L
From: Biju
Date: Mon Jun 22 2026 - 11:58:48 EST
From: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
RZ/G3L supports HS400 enhanced strobe mode, which requires additional
SCC register programming beyond the standard HS400 path.
Introduce a TMIO_MMC_HS400ES flag (bit 17) to identify controllers
that support enhanced strobe. Add renesas_sdhi_hs400_enhanced_strobe()
which, when ios->enhanced_strobe is set, disables DTSEL in
SCC_CKSEL, clears TAPEN in SCC_DTCNTL, programs SCC_TMPPORT3,
sets HWADJ2 to 0xFF, enables the HS400 interface mode bit in
CTL_SDIF_MODE, sets HS400EN2 in HS400MODE2, and raises both
HS400EN and the new HS400MODE1_ENHANCED_STROBE bit (BIT(30)) in
TMPPORT2. On exit from enhanced strobe, only the enhanced strobe bit
is cleared. The callback is registered as
host->ops.hs400_enhanced_strobe for all SCC-capable controllers.
Update renesas_sdhi_reset_hs400_mode() to also mask off
HS400MODE1_ENHANCED_STROBE from TMPPORT2 when TMIO_MMC_HS400ES is
set, ensuring a clean reset on mode exit.
Enable TMIO_MMC_HS400ES in of_data_rzg3l.
Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
v18:
* New patch.
---
drivers/mmc/host/renesas_sdhi_core.c | 48 +++++++++++++++++--
drivers/mmc/host/renesas_sdhi_internal_dmac.c | 2 +-
include/linux/platform_data/tmio.h | 3 ++
3 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index efc8bd1d2422..f1acdf07e8f0 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -299,7 +299,8 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc)
#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP BIT(24)
#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR (BIT(8) | BIT(24))
-#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31)
+#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31)
+#define SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE BIT(30)
/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT4 register */
#define SH_MOBILE_SDHI_SCC_TMPPORT4_DLL_ACC_START BIT(0)
@@ -580,6 +581,8 @@ static void renesas_sdhi_adjust_hs400_mode_disable(struct tmio_mmc_host *host)
static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
struct renesas_sdhi *priv)
{
+ u32 val = ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | host->pdata->osel_tmpout);
+
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
@@ -589,10 +592,11 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos);
+ if (host->pdata->flags & TMIO_MMC_HS400ES)
+ val &= ~SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE;
+
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2,
- ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
- host->pdata->osel_tmpout) &
- sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
+ val & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
if (host->pdata->flags & TMIO_MMC_HS400MODE2)
sd_scc_write32(host, priv, RZG3L_SDHI_SCC_HS400MODE2, 0x0);
@@ -797,6 +801,41 @@ static int renesas_sdhi_execute_tuning(struct mmc_host *mmc, u32 opcode)
return ret;
}
+static void renesas_sdhi_hs400_enhanced_strobe(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct renesas_sdhi *priv = host_to_priv(host);
+ u32 val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2);
+
+ if (!(host->pdata->flags & TMIO_MMC_HS400ES))
+ return;
+
+ if (ios->enhanced_strobe) {
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
+ ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL &
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL,
+ ~SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN &
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT3, BIT(8) | BIT(9));
+ sd_scc_write32(host, priv, RZG3L_SDHI_SCC_HWADJ2, 0xFF);
+ sd_ctrl_write16(host, CTL_SDIF_MODE, SDIF_MODE_HS400 |
+ sd_ctrl_read16(host, CTL_SDIF_MODE));
+ sd_scc_write32(host, priv, RZG3L_SDHI_SCC_HS400MODE2,
+ RZG3L_SDHI_SCC_HS400MODE2_HS400EN2);
+
+ val |= SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
+ SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE;
+ } else {
+ val &= ~SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE;
+ }
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, val);
+}
+
static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap)
{
struct renesas_sdhi *priv = host_to_priv(host);
@@ -1355,6 +1394,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning;
host->ops.hs400_downgrade = renesas_sdhi_disable_scc;
host->ops.hs400_complete = renesas_sdhi_hs400_complete;
+ host->ops.hs400_enhanced_strobe = renesas_sdhi_hs400_enhanced_strobe;
}
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all);
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index bce8f4bb6cf2..b342d963032a 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -176,7 +176,7 @@ static const struct renesas_sdhi_of_data of_data_rzg3l = {
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
TMIO_MMC_64BIT_DATA_PORT | TMIO_MMC_TUNING_DELAY |
TMIO_MMC_INTERNAL_DIVIDER | TMIO_MMC_HWADJ2 |
- TMIO_MMC_HS400MODE2,
+ TMIO_MMC_HS400MODE2 | TMIO_MMC_HS400ES,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
diff --git a/include/linux/platform_data/tmio.h b/include/linux/platform_data/tmio.h
index fe8cdc057e5a..c74b9865e65d 100644
--- a/include/linux/platform_data/tmio.h
+++ b/include/linux/platform_data/tmio.h
@@ -62,6 +62,9 @@
/* Some controllers have HS400mode2 */
#define TMIO_MMC_HS400MODE2 BIT(16)
+/* Some controllers have HS400ES */
+#define TMIO_MMC_HS400ES BIT(17)
+
struct tmio_mmc_data {
void *chan_priv_tx;
void *chan_priv_rx;
--
2.43.0