RE: [PATCH v19 08/12] mmc: renesas_sdhi: Add RZ/G3L SDHI support

Biju Das <[email protected]> Fri, 10 Jul 2026 08:53:29 +0000
Newsgroups org.kernel.vger.linux-mmc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-renesas-soc
Message-ID <TY3PR01MB113461CAEBBBE8B42E417D28E86FD2@TY3PR01MB11346.jpnprd01.prod.outlook.com>
Hi All,

> -----Original Message-----
> From: Biju <[email protected]>
> Sent: 09 July 2026 20:29
> Subject: [PATCH v19 08/12] mmc: renesas_sdhi: Add RZ/G3L SDHI support
> 
> From: Biju Das <[email protected]>
> 
> The RZ/G3L SoC (r9a08g046) has an SDHI controller with several differences from existing platforms,
> requiring dedicated handling.
> 
> Utilise the TMIO_MMC_HWADJ flag to identify controllers that carry a second hardware adjustment register
> (SDm_SCC_HWADJ2 at offset 0x010).
> When both TMIO_MMC_TUNING_DELAY and TMIO_MMC_HWADJ are set,
> renesas_sdhi_set_hw_adjustment_delay() additionally programs HWADJ2 with 0x3FFF at 3.3 V and 0xFF at 1.8
> V. A new RZG3L_SDHI_SCC_HWADJ4 register (offset 0x022) is also cleared at the start of tuning when
> TMIO_MMC_INTERNAL_DIVIDER is set.
> 
> Add a new version constant SDHI_VER_RZ_G3L_SDMMC (0xce10) and extend
> renesas_sdhi_sdbuf_width() to handle it alongside the existing Gen3 variants.
> 
> Introduce of_data_rzg3l with RZ/G3L-specific parameters: a wider clk_mask of 0x200000200, a max_divider
> of 2048 (reflecting the 11-bit divider), a dedicated rzg3l_scc_taps table, and all relevant tmio_flags,
> including TMIO_MMC_INTERNAL_DIVIDER and TMIO_MMC_HWADJ. Wire it to a new of_rzg3l_compatible entry using
> sdhi_quirks_rzg3l (which sets fixed_addr_mode), and register the "renesas,sdhi-r9a08g046" compatible
> string in the OF match table.
> 
> Signed-off-by: Biju Das <[email protected]>
> ---
> v18->v19:
>  * Update commit description.
>  * Updated handling for clk divider.
>  * Renamed TMIO_MMC_HWADJ2->TMIO_MMC_HWADJ to make it generic for
>    hardware tuning delays
>  * Dropped duplicate SH_MOBILE_SDHI_SCC_TMPPORT2 macro
> v18:
>  * New patch.
> ---
>  drivers/mmc/host/renesas_sdhi_core.c          | 28 ++++++++++++--
>  drivers/mmc/host/renesas_sdhi_internal_dmac.c | 37 +++++++++++++++++++
>  include/linux/platform_data/tmio.h            |  3 ++
>  3 files changed, 64 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
> index 6ec8dc96b581..c4908c09d89d 100644
> --- a/drivers/mmc/host/renesas_sdhi_core.c
> +++ b/drivers/mmc/host/renesas_sdhi_core.c
> @@ -59,6 +59,7 @@
>  #define SDHI_VER_GEN2_SDR104	0xcb0d
>  #define SDHI_VER_GEN3_SD	0xcc10
>  #define SDHI_VER_GEN3_SDMMC	0xcd10
> +#define SDHI_VER_RZ_G3L_SDMMC	0xce10
> 
>  #define SDHI_GEN3_MMC0_ADDR	0xee140000
> 
> @@ -79,6 +80,7 @@ static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
>  		break;
>  	case SDHI_VER_GEN3_SD:
>  	case SDHI_VER_GEN3_SDMMC:
> +	case SDHI_VER_RZ_G3L_SDMMC:
>  		if (width == 64)
>  			val = HOST_MODE_GEN3_64BIT;
>  		else if (width == 32)
> @@ -234,7 +236,14 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
>  	}
> 
>  	clock = clk & CLK_CTL_DIV_MASK;
> -	if (clock != CLK_CTL_DIV_MASK)
> +	if (host->pdata->max_divider != SDHI_MAX_DIVIDER_DEFAULT) {
> +		u64 tmp;
> +
> +		tmp = FIELD_GET(GENMASK(9, 8), clk);
> +		clock |= FIELD_PREP(GENMASK(17, 16), tmp);
> +	}
> +

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

There is some issue with the logic here as pointed out by sashiko[1].
Will fix it in next version.

[1]
https://sashiko.dev/#/patchset/20260709192916.630794-1-biju.das.jz%40bp.renesas.com

Cheers,
Biju

> 
>  	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clock); @@ -273,12 +282,14 @@ static int
> renesas_sdhi_card_busy(struct mmc_host *mmc)
>  #define SH_MOBILE_SDHI_SCC_RVSCNTL	0x008
>  #define SH_MOBILE_SDHI_SCC_RVSREQ	0x00A
>  #define SH_MOBILE_SDHI_SCC_SMPCMP       0x00C
> -#define SH_MOBILE_SDHI_SCC_TMPPORT2	0x00E
> +#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_TMPPORT4	0x016
>  #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_HWADJ4		0x022
> 
>  #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN		BIT(0)
>  #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT	16
> @@ -328,14 +339,20 @@ static inline void sd_scc_write32(struct tmio_mmc_host *host,  static void
> renesas_sdhi_set_hw_adjustment_delay(struct tmio_mmc_host *host)  {
>  	struct renesas_sdhi *priv = host_to_priv(host);
> +	bool hwadj2 = host->pdata->flags & TMIO_MMC_HWADJ;
> 
>  	if (!(host->pdata->flags & TMIO_MMC_TUNING_DELAY))
>  		return;
> 
> -	if (host->mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330)
> +	if (host->mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
>  		sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, 0x0);
> -	else
> +		if (hwadj2)
> +			sd_scc_write32(host, priv, RZG3L_SDHI_SCC_HWADJ2, 0x3FFF);
> +	} else {
>  		sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, 0x1);
> +		if (hwadj2)
> +			sd_scc_write32(host, priv, RZG3L_SDHI_SCC_HWADJ2, 0xFF);
> +	}
>  }
> 
>  static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, @@ -401,6 +418,9 @@ static
> unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host)
> 
>  	sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos);
> 
> +	if (host->pdata->flags & TMIO_MMC_HWADJ)
> +		sd_scc_write32(host, priv, RZG3L_SDHI_SCC_HWADJ4, 0x0);
> +
>  	sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
>  			sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
> 
> diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> index 8476678d47a6..e78dbcb6c887 100644
> --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
> @@ -88,6 +88,13 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
>  	},
>  };
> 
> +static struct renesas_sdhi_scc rzg3l_scc_taps[] = {
> +	{
> +		.clk_rate = 0,
> +		.tap = 0x00000300,
> +	},
> +};
> +
>  static const struct renesas_sdhi_of_data of_data_rza2 = {
>  	.tmio_flags	= TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
>  			  TMIO_MMC_HAVE_CBSY,
> @@ -161,6 +168,26 @@ static const struct renesas_sdhi_of_data of_data_rz_g2l = {
>  	.max_divider	= SDHI_MAX_DIVIDER_DEFAULT,
>  };
> 
> +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_HWADJ,
> +	.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		= rzg3l_scc_taps,
> +	.taps_num	= ARRAY_SIZE(rzg3l_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	= 0x200000200,
> +	.max_divider	= 2048,
> +};
> +
>  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 }, @@ -241,6 +268,10 @@ static
> const struct renesas_sdhi_quirks sdhi_quirks_rzg2l = {
>  	.hs400_disabled = true,
>  };
> 
> +static const struct renesas_sdhi_quirks sdhi_quirks_rzg3l = {
> +	.fixed_addr_mode = true,
> +};
> +
>  /*
>   * Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
>   * So, we want to treat them equally and only have a match for ES1.2 to enforce @@ -300,6 +331,11 @@
> static const struct renesas_sdhi_of_data_with_quirks of_rza2_compatible = {
>  	.quirks = &sdhi_quirks_fixed_addr,
>  };
> 
> +static const struct renesas_sdhi_of_data_with_quirks of_rzg3l_compatible = {
> +	.of_data = &of_data_rzg3l,
> +	.quirks = &sdhi_quirks_rzg3l,
> +};
> +
>  static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
>  	{ .compatible = "renesas,sdhi-r7s9210", .data = &of_rza2_compatible, },
>  	{ .compatible = "renesas,sdhi-mmc-r8a77470", .data = &of_rcar_gen3_compatible, }, @@ -313,6 +349,7
> @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
>  	{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
>  	{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
>  	{ .compatible = "renesas,sdhi-r8a779md", .data = &of_rcar_gen3_nohs400_compatible, },
> +	{ .compatible = "renesas,sdhi-r9a08g046", .data =
> +&of_rzg3l_compatible, },
>  	{ .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, },
>  	{ .compatible = "renesas,sdhi-r9a09g057", .data = &of_rzg2l_compatible, },
>  	{ .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, }, diff --git
> a/include/linux/platform_data/tmio.h b/include/linux/platform_data/tmio.h
> index 6c512e96e192..e6663cc736be 100644
> --- a/include/linux/platform_data/tmio.h
> +++ b/include/linux/platform_data/tmio.h
> @@ -56,6 +56,9 @@
>  /* Some controllers have internal divider */
>  #define TMIO_MMC_INTERNAL_DIVIDER	BIT(14)
> 
> +/* Some controllers have hw adjustment delay */
> +#define TMIO_MMC_HWADJ			BIT(15)
> +
>  struct tmio_mmc_data {
>  	void				*chan_priv_tx;
>  	void				*chan_priv_rx;
> --
> 2.43.0