[PATCH v18 05/12] mmc: renesas_sdhi: Add tuning delay support for RZ/G2L

From: Biju

Date: Mon Jun 22 2026 - 11:57:14 EST


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

Some SoCs, starting with RZ/G2L, require a hardware adjustment delay to be
written to SCC_TMPPORT2 when switching signal voltage during tuning. The
register value differs by voltage: 0x1 for 1.8 V and 0x0 for 3.3 V.

Introduce TMIO_MMC_TUNING_DELAY flag (bit 13) in tmio.h to identify
controllers that need this behaviour. Add
renesas_sdhi_set_hw_adjustment_delay(), which writes the appropriate
value to SCC_TMPPORT2 when the flag is set, and call it from
renesas_sdhi_start_signal_voltage_switch() after the regulator is
configured. Additionally, reset SCC_TMPPORT2 to 0 at the start of
renesas_sdhi_execute_tuning() when the flag is set and tap_num is 8.

Introduce a dedicated of_data_rz_g2l descriptor carrying the new flag
alongside RZ/G2L-specific capabilities, and wire of_rzg2l_compatible
to it instead of the generic R-Car Gen3 descriptor.

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

diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 7e48e78cbfab..e9767aa83b00 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -256,40 +256,6 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc)
TMIO_STAT_DAT0);
}

-static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
- struct mmc_ios *ios)
-{
- struct tmio_mmc_host *host = mmc_priv(mmc);
- struct renesas_sdhi *priv = host_to_priv(host);
- struct pinctrl_state *pin_state;
- int ret;
-
- switch (ios->signal_voltage) {
- case MMC_SIGNAL_VOLTAGE_330:
- pin_state = priv->pins_default;
- break;
- case MMC_SIGNAL_VOLTAGE_180:
- pin_state = priv->pins_uhs;
- break;
- default:
- return -EINVAL;
- }
-
- /*
- * If anything is missing, assume signal voltage is fixed at
- * 3.3V and succeed/fail accordingly.
- */
- if (IS_ERR(priv->pinctrl) || IS_ERR(pin_state))
- return ios->signal_voltage ==
- MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL;
-
- ret = mmc_regulator_set_vqmmc(host->mmc, ios);
- if (ret < 0)
- return ret;
-
- return pinctrl_select_state(priv->pinctrl, pin_state);
-}
-
/* SCC registers */
#define SH_MOBILE_SDHI_SCC_DTCNTL 0x000
#define SH_MOBILE_SDHI_SCC_TAPSET 0x002
@@ -350,6 +316,55 @@ static inline void sd_scc_write32(struct tmio_mmc_host *host,
writel(val, priv->scc_ctl + (addr << host->bus_shift));
}

+static void renesas_sdhi_set_hw_adjustment_delay(struct tmio_mmc_host *host)
+{
+ struct renesas_sdhi *priv = host_to_priv(host);
+
+ if (!(host->pdata->flags & TMIO_MMC_TUNING_DELAY))
+ return;
+
+ if (host->mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, 0x0);
+ else
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, 0x1);
+}
+
+static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct renesas_sdhi *priv = host_to_priv(host);
+ struct pinctrl_state *pin_state;
+ int ret;
+
+ switch (ios->signal_voltage) {
+ case MMC_SIGNAL_VOLTAGE_330:
+ pin_state = priv->pins_default;
+ break;
+ case MMC_SIGNAL_VOLTAGE_180:
+ pin_state = priv->pins_uhs;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * If anything is missing, assume signal voltage is fixed at
+ * 3.3V and succeed/fail accordingly.
+ */
+ if (IS_ERR(priv->pinctrl) || IS_ERR(pin_state))
+ return ios->signal_voltage ==
+ MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL;
+
+ ret = mmc_regulator_set_vqmmc(host->mmc, ios);
+ if (ret < 0)
+ return ret;
+
+ renesas_sdhi_set_hw_adjustment_delay(host);
+
+ return pinctrl_select_state(priv->pinctrl, pin_state);
+}
+
static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host)
{
struct renesas_sdhi *priv;
@@ -709,6 +724,9 @@ 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 (priv->tap_num * 2 >= sizeof(priv->taps) * BITS_PER_BYTE) {
dev_err(&host->pdev->dev,
"Too many taps, please update 'taps' in tmio_mmc_host!\n");
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index c91b910488da..93219706a4d6 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -143,6 +143,25 @@ static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = {
.max_divider = SDHI_MAX_DIVIDER_DEFAULT,
};

+static const struct renesas_sdhi_of_data of_data_rz_g2l = {
+ .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,
+ .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,
+ .bus_shift = 2,
+ .scc_offset = 0x1000,
+ .taps = rcar_gen3_scc_taps,
+ .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
+ /* DMAC can handle 32bit blk count but only 1 segment */
+ .max_blk_count = UINT_MAX / TMIO_MAX_BLK_SIZE,
+ .max_segs = 1,
+ .sdhi_flags = SDHI_FLAG_NEED_CLKH_FALLBACK,
+ .clk_mask = SDHI_CLK_MASK_DEFAULT,
+ .max_divider = SDHI_MAX_DIVIDER_DEFAULT,
+};
+
static const u8 r8a7796_es13_calib_table[2][SDHI_CALIB_TABLE_MAX] = {
{ 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 15,
16, 16, 16, 16, 16, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25 },
@@ -264,7 +283,7 @@ static const struct renesas_sdhi_of_data_with_quirks of_r8a77990_compatible = {
};

static const struct renesas_sdhi_of_data_with_quirks of_rzg2l_compatible = {
- .of_data = &of_data_rcar_gen3,
+ .of_data = &of_data_rz_g2l,
.quirks = &sdhi_quirks_rzg2l,
};

diff --git a/include/linux/platform_data/tmio.h b/include/linux/platform_data/tmio.h
index 27ea21c00419..868a21842fa5 100644
--- a/include/linux/platform_data/tmio.h
+++ b/include/linux/platform_data/tmio.h
@@ -50,6 +50,9 @@
/* Some controllers have a 64-bit wide data port register */
#define TMIO_MMC_64BIT_DATA_PORT BIT(12)

+/* Some controllers have tuning delay */
+#define TMIO_MMC_TUNING_DELAY BIT(13)
+
struct tmio_mmc_data {
void *chan_priv_tx;
void *chan_priv_rx;
--
2.43.0