From 5a52c5701a67d5176eb1afbf1bdaf7d6dfeec597 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Thu, 23 Apr 2026 15:18:55 -0400 Subject: mmc: core: Fix host controller programming for fixed driver type When using the fixed-emmc-driver-type device tree property, the MMC core correctly selects the driver strength for the card but fails to program the host controller accordingly. This causes a mismatch where the card uses the specified driver type while the host controller defaults to Type B (since ios->drv_type remains zero). Split the driver type programming logic to handle both fixed and dynamic driver type selection paths. For fixed driver types, program the host controller with the selected drive_strength value. For dynamic selection, use the existing drv_type as before. This ensures both the eMMC device and host controller use matching driver strengths, preventing potential signal integrity issues. Fixes: 6186d06c519e ("mmc: parse new binding for eMMC fixed driver type") Signed-off-by: Kamal Dasu Reviewed-by: Shawn Lin Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 8846550a8892..05444ecf3909 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1371,7 +1371,9 @@ static void mmc_select_driver_type(struct mmc_card *card) card->drive_strength = drive_strength; - if (drv_type) + if (fixed_drv_type >= 0 && drive_strength) + mmc_set_driver_type(card->host, drive_strength); + else if (drv_type) mmc_set_driver_type(card->host, drv_type); } -- cgit v1.2.3 From 8d4ae34e997062076a9098602eaca43353665bd9 Mon Sep 17 00:00:00 2001 From: Huan He Date: Sat, 9 May 2026 16:49:07 +0800 Subject: mmc: sdhci-of-dwcmshc: Fix reset, clk, and SDIO support for Eswin EIC7700 The EIC7700 code in sdhci-of-dwcmshc uses host->mmc->caps2 to select different configuration paths for different card types. The current logic distinguishes eMMC and SD, but does not handle SDIO separately. Update the EIC7700 card-type checks so that eMMC, SD and SDIO are distinguished explicitly. Switch the reset path to dwcmshc_reset() so that pending interrupt state is cleared consistently, and use sdhci_enable_clk() so the clock enable sequence follows the standard SDHCI flow. Fixes: 32b2633219d3 ("mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700") Signed-off-by: Huan He Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 0b2158a7e409..b9ecd91f44ad 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -277,6 +277,7 @@ #define PHY_DELAY_CODE_MAX 0x7f #define PHY_DELAY_CODE_EMMC 0x17 #define PHY_DELAY_CODE_SD 0x55 +#define PHY_DELAY_CODE_SDIO 0x29 struct rk35xx_priv { struct reset_control *reset; @@ -1433,10 +1434,7 @@ static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) clk_set_rate(pltfm_host->clk, clock); clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk |= SDHCI_CLOCK_INT_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - dwcmshc_enable_card_clk(host); + sdhci_enable_clk(host, clk); } static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay) @@ -1497,7 +1495,7 @@ static void sdhci_eic7700_config_phy(struct sdhci_host *host) static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask) { - sdhci_reset(host, mask); + dwcmshc_reset(host, mask); /* after reset all, the phy's config will be clear */ if (mask == SDHCI_RESET_ALL) @@ -1594,18 +1592,17 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; int phase_code = -1; int code_range = -1; - bool is_sd = false; int code_min = -1; int code_max = -1; int cmd_error = 0; + bool is_emmc; int ret = 0; int i = 0; - if ((host->mmc->caps2 & sd_caps) == sd_caps) - is_sd = true; + is_emmc = (host->mmc->caps2 & emmc_caps) == emmc_caps; for (i = 0; i <= MAX_PHASE_CODE; i++) { /* Centered Phase code */ @@ -1614,8 +1611,8 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); if (ret) { - /* SD specific range tracking */ - if (is_sd && code_min != -1 && code_max != -1) { + /* SD/SDIO specific range tracking */ + if (!is_emmc && code_min != -1 && code_max != -1) { if (code_max - code_min > code_range) { code_range = code_max - code_min; phase_code = (code_min + code_max) / 2; @@ -1626,17 +1623,17 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) code_max = -1; } /* EMMC breaks after first valid range */ - if (!is_sd && code_min != -1 && code_max != -1) + if (is_emmc && code_min != -1 && code_max != -1) break; } else { /* Track valid phase code range */ if (code_min == -1) { code_min = i; - if (!is_sd) + if (is_emmc) continue; } code_max = i; - if (is_sd && i == MAX_PHASE_CODE) { + if (!is_emmc && i == MAX_PHASE_CODE) { if (code_max - code_min > code_range) { code_range = code_max - code_min; phase_code = (code_min + code_max) / 2; @@ -1646,19 +1643,19 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) } /* Handle tuning failure case */ - if ((is_sd && phase_code == -1) || - (!is_sd && code_min == -1 && code_max == -1)) { + if ((!is_emmc && phase_code == -1) || + (is_emmc && code_min == -1 && code_max == -1)) { pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc)); sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); return -EIO; } - if (!is_sd) + if (is_emmc) phase_code = (code_min + code_max) / 2; sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); - /* SD specific final verification */ - if (is_sd) { + /* SD/SDIO specific final verification */ + if (!is_emmc) { ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); if (ret) { @@ -1756,9 +1753,9 @@ static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned in static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing) { - u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; - if ((host->mmc->caps2 & sd_caps) == sd_caps) + if ((host->mmc->caps2 & emmc_caps) != emmc_caps) sdhci_set_uhs_signaling(host, timing); else sdhci_eic7700_set_uhs_signaling(host, timing); @@ -1767,6 +1764,7 @@ static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; unsigned int val, hsp_int_status, hsp_pwr_ctrl; static const char * const clk_ids[] = {"axi"}; struct of_phandle_args args; @@ -1821,8 +1819,10 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm if ((host->mmc->caps2 & emmc_caps) == emmc_caps) dwc_priv->delay_line = PHY_DELAY_CODE_EMMC; - else + else if ((host->mmc->caps2 & sd_caps) == sd_caps) dwc_priv->delay_line = PHY_DELAY_CODE_SD; + else + dwc_priv->delay_line = PHY_DELAY_CODE_SDIO; if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val)) priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val); -- cgit v1.2.3 From f48ee49726ee4ab545fd2dc644f169c0809b19b3 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 19 May 2026 14:53:40 +0100 Subject: mmc: renesas_sdhi: Add OF entry for RZ/G2H SoC The RZ/G2H (R8A774E1) SoC was previously handled via the generic "renesas,rcar-gen3-sdhi" fallback compatible string. However, because the SDHI IP on RZ/G2H is identical with the R-Car H3-N (R8A77951), it requires the specific quirks and configuration defined in `of_r8a7795_compatible` rather than the generic Gen3 data. Add the explicit "renesas,sdhi-r8a774e1" match entry to map it correctly. Note that the DT binding file renesas,sdhi.yaml does not need an update as the entry for this SoC is already present. Fixes: 31941342888d ("arm64: dts: renesas: r8a774e1: Add SDHI nodes") Cc: stable@vger.kernel.org Signed-off-by: Lad Prabhakar Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index f6ebb7bc7ede..838248bf8dd6 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -279,6 +279,7 @@ static const struct renesas_sdhi_of_data_with_quirks of_rza2_compatible = { 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, }, + { .compatible = "renesas,sdhi-r8a774e1", .data = &of_r8a7795_compatible, }, { .compatible = "renesas,sdhi-r8a7795", .data = &of_r8a7795_compatible, }, { .compatible = "renesas,sdhi-r8a77961", .data = &of_r8a77961_compatible, }, { .compatible = "renesas,sdhi-r8a77965", .data = &of_r8a77965_compatible, }, -- cgit v1.2.3 From b837e38c255dd9f8b53511d52e87f1fda32b3dfe Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 21 May 2026 15:21:20 +0800 Subject: mmc: litex_mmc: Use DIV_ROUND_UP for more accurate clock calculation The previous clock uses roundup_pow_of_two() to calculate the core clock frequency. It does not meet the actual hardware meaning. The actual frequency is calculated by "ref_clk / ((div >> 1) << 1)". Fix the clock divider calculation. Fixes: 92e099104729 ("mmc: Add driver for LiteX's LiteSDCard interface") Signed-off-by: Inochi Amaoto Reviewed-by: Gabriel Somlo Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/litex_mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index d2f19c2dc673..52571bb17c61 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -436,11 +437,10 @@ static void litex_mmc_setclk(struct litex_mmc_host *host, unsigned int freq) struct device *dev = mmc_dev(host->mmc); u32 div; - div = freq ? host->ref_clk / freq : 256U; - div = roundup_pow_of_two(div); + div = freq ? DIV_ROUND_UP(host->ref_clk, freq) : 256U; div = clamp(div, 2U, 256U); dev_dbg(dev, "sd_clk_freq=%d: set to %d via div=%d\n", - freq, host->ref_clk / div, div); + freq, host->ref_clk / ((div + 1) & ~1U), div); litex_write16(host->sdphy + LITEX_PHY_CLOCKERDIV, div); host->sd_clk = freq; } -- cgit v1.2.3 From 99982b743e5ba72bd1f5de0e03e3b96ae70b1e51 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 21 May 2026 15:21:21 +0800 Subject: mmc: litex_mmc: Set mandatory idle clocks before CMD0 The litex_mmc driver assumes the card is already probed in the BIOS and skip the phy initialization. This will cause the command fail like the following when the old card is unplugged and then insert a new card: [ 62.923593] litex-mmc f0004000.mmc: Command (cmd 8) error, status -110 [ 62.949717] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 [ 62.976606] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 [ 63.002516] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 [ 63.028442] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 Add required clock settings and initialization for the CMD 0, so it can probe the new card. Fixes: 92e099104729 ("mmc: Add driver for LiteX's LiteSDCard interface") Signed-off-by: Inochi Amaoto Reviewed-by: Gabriel Somlo Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/litex_mmc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 52571bb17c61..3655542ca998 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -69,6 +69,9 @@ #define SD_SLEEP_US 5 #define SD_TIMEOUT_US 20000 +#define SD_INIT_DELAY_US 1000 +#define SD_INIT_CLK_HZ 400000 + #define SDIRQ_CARD_DETECT 1 #define SDIRQ_SD_TO_MEM_DONE 2 #define SDIRQ_MEM_TO_SD_DONE 4 @@ -449,6 +452,17 @@ static void litex_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct litex_mmc_host *host = mmc_priv(mmc); + /* + * The SD specification requires at least 74 idle clocks before CMD0. + * These dummy cycles is generated by writing LITEX_PHY_INITIALIZE. + */ + if (ios->chip_select == MMC_CS_HIGH) { + litex_mmc_setclk(host, SD_INIT_CLK_HZ); + litex_write8(host->sdphy + LITEX_PHY_INITIALIZE, 1); + fsleep(SD_INIT_DELAY_US); + return; + } + /* * NOTE: Ignore any ios->bus_width updates; they occur right after * the mmc core sends its own acmd6 bus-width change notification, -- cgit v1.2.3 From 1e9a4850afa0ceb63984fb1a9f3e86d0fc4fd18f Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 22 May 2026 20:43:07 +0200 Subject: mmc: dw_mmc-rockchip: Add missing private data for very old controllers The really old controllers (rk2928, rk3066, rk3188) do not support UHS speeds at all, and thus never handled phase data. For that reason it never had a parse_dt callback and no driver private data at all. Commit ff6f0286c896 ("mmc: dw_mmc-rockchip: Add memory clock auto-gating support") makes the private data sort of mandatory, because the init function checks whether phases are configured internally or through the clock controller. This results in the old SoCs then experiencing NULL-pointer dereferences when they try to access that private-data struct. While we could have if (priv) conditionals in all places, it's way less cluttery to just give the old types their private-data struct. Fixes: ff6f0286c896 ("mmc: dw_mmc-rockchip: Add memory clock auto-gating support") Cc: stable@vger.kernel.org Signed-off-by: Heiko Stuebner Acked-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index c6eece4ec3fd..75c82ff20f17 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -441,6 +441,22 @@ static int dw_mci_common_parse_dt(struct dw_mci *host) return 0; } +static int dw_mci_rk2928_parse_dt(struct dw_mci *host) +{ + struct dw_mci_rockchip_priv_data *priv; + int err; + + err = dw_mci_common_parse_dt(host); + if (err) + return err; + + priv = host->priv; + + priv->internal_phase = false; + + return 0; +} + static int dw_mci_rk3288_parse_dt(struct dw_mci *host) { struct dw_mci_rockchip_priv_data *priv; @@ -514,6 +530,7 @@ static int dw_mci_rockchip_init(struct dw_mci *host) static const struct dw_mci_drv_data rk2928_drv_data = { .init = dw_mci_rockchip_init, + .parse_dt = dw_mci_rk2928_parse_dt, }; static const struct dw_mci_drv_data rk3288_drv_data = { -- cgit v1.2.3 From f595e8e77a51eee35e331f69321766593a845ef2 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 24 May 2026 10:34:55 +0800 Subject: mmc: sdhci: add signal voltage switch in sdhci_resume_host I met one suspend/resume issue with sdr104 capable sdio wifi card (with "keep-power-in-suspend" set in DT property): After resuming from suspend to ram, the sdio wifi card stops working. Further debug shows that although ios shows the sdio card is at sdr104 mode, the voltage is still at 3V3. This is due to missing the calling of ->start_signal_voltage_switch() in sdhci_resume_host(). Fix this issue by adding ->start_signal_voltage_switch() in sdhci_resume_host(). This also matches what we do for sdhci_runtime_resume_host(). Then the question is: why this issue hasn't reported and fixed for so long time. IMHO, several reasons: Some host controllers just kick off the runtime resume for system resume, so they benefit from the well supported runtime pm code; Some platforms just use the old sdio wifi card which doesn't need signal voltage switch at all, the default voltage is 3v3 after resuming. Fixes: 6308d2905bd3 ("mmc: sdhci: add quirk for keeping card power during suspend") Signed-off-by: Jisheng Zhang Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 605be55f8d2d..e3bf901b10aa 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3836,6 +3836,7 @@ int sdhci_resume_host(struct sdhci_host *host) host->pwr = 0; host->clock = 0; host->reinit_uhs = true; + mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios); mmc->ops->set_ios(mmc, &mmc->ios); } else { sdhci_init(host, (mmc->pm_flags & MMC_PM_KEEP_POWER)); -- cgit v1.2.3