[PATCH 14/17] mmc: renesas_sdhi: Add HS400 enhanced strobe support for RZ/G3L

From: Biju

Date: Sat May 30 2026 - 12:14:16 EST


From: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>

Add an hs400_es bitfield to renesas_sdhi_hw_info and implement
renesas_sdhi_hs400_enhanced_strobe(), registered as
host->ops.hs400_enhanced_strobe for all SCC-capable controllers.

Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
drivers/mmc/host/renesas_sdhi.h | 1 +
drivers/mmc/host/renesas_sdhi_core.c | 53 ++++++++++++++++---
drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 +
3 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index 92b66116f044..1a837d0c9479 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -48,6 +48,7 @@ struct renesas_sdhi_hw_info {
unsigned tuning_delay:1; /* Has tuning delay */
unsigned internal_divider:1; /* Has internal divider */
unsigned scc_hs400_mode2:1; /* Has scc hs400 mode2 */
+ unsigned hs400_es:1; /* Has hs400 enhanced strobe */
};

struct renesas_sdhi_of_data_with_info {
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 282107d06114..2a70a2e64b9c 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -274,7 +274,7 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc)
#define SH_MOBILE_SDHI_SCC_SMPCMP 0x00C
#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E /* G3L: SDm_SCC_HS400MODE1 */
#define RZG3L_SDHI_SCC_HWADJ2 0x010
-#define SH_MOBILE_SDHI_SCC_TMPPORT3 0x014
+#define SH_MOBILE_SDHI_SCC_TMPPORT3 0x014 /* G3L: SDm_SCC_HWADJ3 */
#define SH_MOBILE_SDHI_SCC_TMPPORT4 0x016 /* R-Car */
#define SH_MOBILE_SDHI_SCC_TMPPORT5 0x018 /* R-Car */
#define SH_MOBILE_SDHI_SCC_TMPPORT6 0x01A /* R-Car */
@@ -298,8 +298,9 @@ 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_HS400OSEL BIT(4)
-#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31)
+#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4)
+#define SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE BIT(30)
+#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31)

/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT4 register */
#define SH_MOBILE_SDHI_SCC_TMPPORT4_DLL_ACC_START BIT(0)
@@ -574,6 +575,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)
{
+ unsigned long val;
+
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));

@@ -583,10 +586,12 @@ 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);

+ val = ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL);
+ if (priv->info->hs400_es)
+ val &= ~SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE;
+
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2,
- ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
- SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) &
- sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
+ val & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));

if (priv->info->scc_hs400_mode2)
sd_scc_write32(host, priv, RZG3L_SDHI_SCC_HS400MODE2, 0x0);
@@ -783,6 +788,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 (!priv->info->hs400_es)
+ 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);
@@ -1333,6 +1373,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 83d348fb5eeb..a021ebb46070 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -277,6 +277,7 @@ static const struct renesas_sdhi_hw_info sdhi_hw_info_rzg3l = {
.tuning_delay = 1,
.internal_divider = 1,
.scc_hs400_mode2 = 1,
+ .hs400_es = 1,
};

static const struct renesas_sdhi_of_data_with_quirks of_r8a7795_compatible = {
--
2.43.0