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

Biju Das <[email protected]> Tue, 7 Jul 2026 12:10:42 +0000
Newsgroups org.kernel.vger.linux-mmc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-renesas-soc
Message-ID <TY3PR01MB113464E7293163E4E349FE76586F02@TY3PR01MB11346.jpnprd01.prod.outlook.com>
Hi Wolfram,

> -----Original Message-----
> From: Biju <[email protected]>
> Sent: 22 June 2026 16:56
> Subject: [PATCH v18 11/12] mmc: renesas_sdhi: Add RZ/G3L HS400 support
> 
> From: Biju Das <[email protected]>
> 
> 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 <[email protected]>
> ---
> 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));

Ref: https://sashiko.dev/#/patchset/20260622155610.184271-1-biju.das.jz%40bp.renesas.com

I will drop checking the clock. Also, I have reported an issue with divider control values in
Hardware manual for HS400 mode. I am planning to defer HS400 support, till I get response from
them. I guess it is OK for you.

Cheers,
Biju



> 
>  	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