[PATCH v23 14/14] mmc: renesas_sdhi: Add HS400 enhanced strobe support for RZ/G3L
Biju <[email protected]> Thu, 30 Jul 2026 12:31:52 +0100
| Newsgroups | org.kernel.vger.linux-mmc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-renesas-soc |
|---|---|
| Message-ID | <[email protected]> |
From: Biju Das <[email protected]> RZ/G3L's HS400 enhanced strobe mode needs extra SCC register programming beyond the standard HS400 path. Add a TMIO_MMC_HS400ES flag (bit 17) for controllers that support it. Add renesas_sdhi_hs400_enhanced_strobe(), registered as host->ops.hs400_enhanced_strobe when the flag is set. On enable, it clears DTSEL and TAPEN, programs TMPPORT3 and HWADJ2, sets the HS400 interface bit in SDIF_MODE, sets HS400EN2 in HS400MODE2, and sets HS400EN plus the new HS400MODE1_ENHANCED_STROBE bit in TMPPORT2. On disable, only the enhanced-strobe-related bits are cleared. Also mask off HS400MODE1_ENHANCED_STROBE in renesas_sdhi_reset_hs400_mode() when TMIO_MMC_HS400ES is set, for a clean reset on mode exit. Since a controller reset clears these SCC registers, renesas_sdhi_reset() now re-invokes renesas_sdhi_hs400_enhanced_strobe() after reset whenever TMIO_MMC_HS400ES is set and the controller is currently in HS400 with enhanced strobe active, restoring the enhanced-strobe state instead of losing it across a reset. Enable TMIO_MMC_HS400ES in of_data_rzg3l to finalize RZ/G3L HS400 support. Signed-off-by: Biju Das <[email protected]> --- v22->v23: * No change. v21->v22: * Updated commit description. * Restored the patch after fixing inode corruption. * renesas_sdhi_reset() now restores data strobe register values after reset. v20->v21: * Dropped the patch due to inode corruption during boot. v19->v20: * SD_CLK_CTRL clk enable turned off before updating SCC_CKSEL_DTSEL register. v18->v19: * Updated commit description. * HS400ES support is enabled based on of_data. * Fixed the space in HS400ES comment block. v18: * New patch. --- drivers/mmc/host/renesas_sdhi_core.c | 68 +++++++++++++++++-- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 2 +- include/linux/platform_data/tmio.h | 3 + 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index b68567c93fef..13c24c42f0ec 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -337,7 +337,8 @@ 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_HS400EN BIT(31) +#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31) +#define SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE BIT(30) /* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT4 register */ #define SH_MOBILE_SDHI_SCC_TMPPORT4_DLL_ACC_START BIT(0) @@ -635,6 +636,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) { + u32 val = ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | host->pdata->osel_tmpout); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); @@ -644,10 +647,11 @@ 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); + if (host->pdata->flags & TMIO_MMC_HS400ES) + val &= ~SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE; + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, - ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | - host->pdata->osel_tmpout) & - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); + val & 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); @@ -678,6 +682,53 @@ static void renesas_sdhi_scc_reset(struct tmio_mmc_host *host, struct renesas_sd sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); } +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 (!(host->pdata->flags & TMIO_MMC_HS400ES)) + return; + + if (ios->enhanced_strobe) { + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + 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_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + 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_TMPPORT2_HS400EN | + SH_MOBILE_SDHI_SCC_HS400MODE1_ENHANCED_STROBE); + + 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, 0); + } + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, val); +} + /* only populated for TMIO_MMC_MIN_RCAR2 */ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve) { @@ -702,6 +753,13 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve) /* Unknown why but without polling reset status, it will hang */ read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100, false, priv->rstc); + + /* Restore data strobe registers */ + if ((host->pdata->flags & TMIO_MMC_HS400ES) && + host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && + host->mmc->ios.enhanced_strobe) + renesas_sdhi_hs400_enhanced_strobe(host->mmc, &host->mmc->ios); + /* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */ sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); if (priv->rdev) @@ -1409,6 +1467,8 @@ 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; + if (host->pdata->flags & TMIO_MMC_HS400ES) + 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 8cd3ced88b81..a47f17e9bf81 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -179,7 +179,7 @@ static const struct renesas_sdhi_of_data of_data_rzg3l = { TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | TMIO_MMC_64BIT_DATA_PORT | TMIO_MMC_TUNING_DELAY | TMIO_MMC_INTERNAL_DIVIDER | TMIO_MMC_HWADJ | - TMIO_MMC_HS400MODE2, + TMIO_MMC_HS400MODE2 | TMIO_MMC_HS400ES, .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 88a3744f965a..ca1435441628 100644 --- a/include/linux/platform_data/tmio.h +++ b/include/linux/platform_data/tmio.h @@ -62,6 +62,9 @@ /* Some controllers have HS400MODE2 */ #define TMIO_MMC_HS400MODE2 BIT(16) +/* Some controllers have HS400ES */ +#define TMIO_MMC_HS400ES BIT(17) + struct tmio_mmc_data { void *chan_priv_tx; void *chan_priv_rx; -- 2.43.0