[PATCH v18 11/12] mmc: renesas_sdhi: Add RZ/G3L HS400 support

From: Biju

Date: Mon Jun 22 2026 - 12:01:19 EST


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

Add HS400 support for RZ/G3L SoC.

RZ/G3L requires a dedicated HS400 enable register (SDm_SCC_HS400MODE2
at offset 0x020) in addition to the existing TMPPORT2 path used by
R-Car. Introduce a TMIO_MMC_HS400MODE2 flag (bit 16) to identify
controllers that need this second register.
When the flag is set, renesas_sdhi_hs400_complete() additionally sets
HS400EN2 in HS400MODE2, and renesas_sdhi_reset_hs400_mode() clears
it on exit. During tuning, when both TMIO_MMC_TUNING_DELAY and
TMIO_MMC_HS400MODE2 are set, the lower 16 bits of TMPPORT2 (TMPOUT)
are masked off while preserving the upper bits, replacing the previous
unconditional write of zero.

In renesas_sdhi_clk_update(), force clkh_shift to 1 when
TMIO_MMC_INTERNAL_DIVIDER is active and the timing mode is HS400,
allowing the SoC's internal divider to be bypassed for that mode. Adjust
renesas_sdhi_set_clock() to halve actual_clock when
TMIO_MMC_INTERNAL_DIVIDER is set, and guard the clock divider
calculation against a zero clock value.

Enable TMIO_MMC_HS400MODE2 in of_data_rzg3l, completing HS400
support for RZ/G3L.

Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
v18:
* New patch.
---
drivers/mmc/host/renesas_sdhi_core.c | 33 ++++++++++++++++---
drivers/mmc/host/renesas_sdhi_internal_dmac.c | 3 +-
include/linux/platform_data/tmio.h | 3 ++
3 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 42645480b62f..efc8bd1d2422 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -186,8 +186,13 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,

clk_set_rate(ref_clk, best_freq);

- if (priv->clkh)
+ if (priv->clkh) {
+ if ((host->pdata->flags & TMIO_MMC_INTERNAL_DIVIDER) &&
+ host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
+ clkh_shift = 1;
+
clk_set_rate(priv->clk, (best_freq >> clkh_shift) * priv->divider);
+ }

return clk_get_rate(priv->clk);
}
@@ -206,7 +211,8 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
goto out;
}

- host->mmc->actual_clock = renesas_sdhi_clk_update(host, new_clock);
+ host->mmc->actual_clock = renesas_sdhi_clk_update(host, new_clock) /
+ (host->pdata->flags & TMIO_MMC_INTERNAL_DIVIDER ? 2 : 1);
clock = host->mmc->actual_clock / host->pdata->max_divider;

/*
@@ -227,7 +233,7 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
}

clock = clk & CLK_CTL_DIV_MASK;
- if (clock != CLK_CTL_DIV_MASK)
+ if (clock != CLK_CTL_DIV_MASK && clock != 0)
host->mmc->actual_clock /= (1 << (ffs(clock) + 1));

sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clock);
@@ -274,6 +280,7 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc)
#define SH_MOBILE_SDHI_SCC_TMPPORT5 0x018
#define SH_MOBILE_SDHI_SCC_TMPPORT6 0x01A
#define SH_MOBILE_SDHI_SCC_TMPPORT7 0x01C
+#define RZG3L_SDHI_SCC_HS400MODE2 0x020
#define RZG3L_SDHI_SCC_HWADJ4 0x022

#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0)
@@ -306,6 +313,7 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc)
#define SH_MOBILE_SDHI_SCC_TMPPORT_DISABLE_WP_CODE 0xa5000000
#define SH_MOBILE_SDHI_SCC_TMPPORT_CALIB_CODE_MASK 0x1f
#define SH_MOBILE_SDHI_SCC_TMPPORT_MANUAL_MODE BIT(7)
+#define RZG3L_SDHI_SCC_HS400MODE2_HS400EN2 BIT(0)

static inline u32 sd_scc_read32(struct tmio_mmc_host *host,
struct renesas_sdhi *priv, int addr)
@@ -441,6 +449,10 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
host->pdata->osel_tmpout) |
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,
+ RZG3L_SDHI_SCC_HS400MODE2_HS400EN2);
+
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL,
SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
sd_scc_read32(host, priv,
@@ -582,6 +594,9 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
host->pdata->osel_tmpout) &
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);
+
if (sdhi_has_quirk(priv, hs400_calib_table) || sdhi_has_quirk(priv, hs400_bad_taps))
renesas_sdhi_adjust_hs400_mode_disable(host);

@@ -739,8 +754,16 @@ static int renesas_sdhi_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (!priv->tap_num)
return 0; /* Tuning is not supported */

- if ((host->pdata->flags & TMIO_MMC_TUNING_DELAY) && priv->tap_num == 8)
- sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, 0);
+ if ((host->pdata->flags & TMIO_MMC_TUNING_DELAY) && priv->tap_num == 8) {
+ u32 val = 0;
+
+ if (host->pdata->flags & TMIO_MMC_HS400MODE2) {
+ val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2);
+ val &= ~GENMASK(15, 0); /* TMPOUT MASK */
+ }
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, val);
+ }

if (priv->tap_num * 2 >= sizeof(priv->taps) * BITS_PER_BYTE) {
dev_err(&host->pdev->dev,
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 709351693efa..bce8f4bb6cf2 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -175,7 +175,8 @@ static const struct renesas_sdhi_of_data of_data_rzg3l = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
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_INTERNAL_DIVIDER | TMIO_MMC_HWADJ2 |
+ TMIO_MMC_HS400MODE2,
.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 8b4032b24d36..fe8cdc057e5a 100644
--- a/include/linux/platform_data/tmio.h
+++ b/include/linux/platform_data/tmio.h
@@ -59,6 +59,9 @@
/* Some controllers have hw adjustment delay */
#define TMIO_MMC_HWADJ2 BIT(15)

+/* Some controllers have HS400mode2 */
+#define TMIO_MMC_HS400MODE2 BIT(16)
+
struct tmio_mmc_data {
void *chan_priv_tx;
void *chan_priv_rx;
--
2.43.0