diff options
| author | Tom Rini <[email protected]> | 2026-07-06 08:10:22 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-07-06 08:10:22 -0600 |
| commit | a25c9bd19215ddd95d7494ebf8d842c46fdf3ba8 (patch) | |
| tree | 7538965d08232f616a1f021314ce403735abbb82 | |
| parent | 780084d7c07f511da4a39b43277c573156ed42e0 (diff) | |
| parent | 3e0208cc64966b2a5049b3b3c6d64c05a018698f (diff) | |
Merge tag 'mmc-for-2026.07' of https://source.denx.de/u-boot/custodians/u-boot-mmc
CI: https://source.denx.de/u-boot/custodians/u-boot-mmc/-/pipelines/30621
- Fix redundant 1.8V voltage switch on cold boot with UHS card
- Revert "mmc: sdhci-cadence: trigger tuning for SD HS mode on SD6HC (v6) PHY"
| -rw-r--r-- | drivers/mmc/mmc.c | 7 | ||||
| -rw-r--r-- | drivers/mmc/sdhci-cadence.c | 108 | ||||
| -rw-r--r-- | drivers/mmc/sdhci-cadence.h | 5 | ||||
| -rw-r--r-- | drivers/mmc/sdhci-cadence6.c | 45 |
4 files changed, 6 insertions, 159 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index f0e38efb262..2e565560656 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2721,10 +2721,11 @@ static int mmc_startup(struct mmc *mmc) #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) /* - * If the card has already switched to 1.8V signaling, then - * set the signal voltage to 1.8V. + * If voltage switch was skipped during ACMD41 but the card is + * already at 1.8V (retained from a previous session, e.g. warm + * reboot), re-configure the host to match. */ - if (mmc_sd_card_using_v18(mmc)) { + if (!(mmc->ocr & OCR_S18R) && mmc_sd_card_using_v18(mmc)) { /* * During a signal voltage level switch, the clock must be gated * for 5 ms according to the SD spec. diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c index a76f9e8d6bd..5bbc18dfa51 100644 --- a/drivers/mmc/sdhci-cadence.c +++ b/drivers/mmc/sdhci-cadence.c @@ -39,9 +39,6 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, }, }; -static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev, - unsigned int opcode); - static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat, u8 addr, u8 data) { @@ -158,93 +155,8 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host) sdhci_cdns6_phy_adj(mmc->dev, plat, mmc->selected_mode); } -static __maybe_unused bool sdhci_cdns_sd_needs_tuning(struct mmc *mmc) -{ - struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev); - - if (!IS_SD(mmc)) - return false; - - if (!dev_read_bool(mmc->dev, "cdns,sd-hs-tuning")) - return false; - - /* Already tuned for this mode */ - if (plat->tuned_mode == mmc->selected_mode) - return false; - - switch (mmc->selected_mode) { - case SD_HS: - return mmc->bus_width == 4; - /* Add future modes here, e.g.: - * case UHS_SDR50: - * return true; - */ - default: - return false; - } -} - -static int sdhci_cdns_set_ios_post(struct sdhci_host *host) -{ - struct mmc *mmc = host->mmc; - struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev); - int ret __maybe_unused; - /* - * The SD6HC soft PHY requires runtime DLL delay calibration - * for SD High Speed mode. The default PHY_DLL_SLAVE_CTRL_REG - * values (READ_DQS_CMD_DELAY and READ_DQS_DELAY = 0) do not - * provide sufficient timing margin due to PVT and board trace - * variations. - * - * Tuning is performed once per entry into SD_HS mode - * (tracked by plat->tuned_mode state). The calibrated PHY delay - * values remain valid while the card stays in SD_HS mode, and - * leaving that tuned mode clears the state so re-entering SD_HS - * triggers tuning again. - * - * This must be done in set_ios_post (not set_control_reg) - * because the SDHCI controller must already be operating at - * the target bus width, clock, and speed mode before CMD19 - * tuning commands can succeed. - */ - - if (IS_ENABLED(CONFIG_MMC_SUPPORTS_TUNING)) { - if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_420 && - sdhci_cdns_sd_needs_tuning(mmc)) { - ret = sdhci_cdns_execute_tuning(mmc->dev, - MMC_CMD_SEND_TUNING_BLOCK); - if (ret) { - dev_err(mmc->dev, - "SD_HS tuning failed (ret=%d), using default PHY\n", - ret); - /* Restore default PHY settings and avoid retrying in this mode */ - sdhci_cdns6_phy_adj(mmc->dev, plat, - mmc->selected_mode); - plat->tuned_mode = mmc->selected_mode; - plat->tuned_dll_slave_ctrl = sdhci_cdns6_phy_get_dll_slave(plat); - return 0; - } - /* - * Tuning succeeded. The tuned_mode is already set by - * execute_tuning(), so the tuned value will be preserved - * across subsequent PHY reconfigurations. - */ - dev_dbg(mmc->dev, "SD_HS tuning successful\n"); - } - - /* Reset when mode changes away from a tuned mode */ - if (mmc->selected_mode != plat->tuned_mode) { - plat->tuned_mode = MMC_MODES_END; - plat->tuned_dll_slave_ctrl = 0; - } - } - - return 0; -} - static const struct sdhci_ops sdhci_cdns_ops = { .set_control_reg = sdhci_cdns_set_control_reg, - .set_ios_post = sdhci_cdns_set_ios_post, }; static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat, @@ -292,7 +204,6 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev, int cur_streak = 0; int max_streak = 0; int end_of_streak = 0; - int ret; int i; /* @@ -318,24 +229,7 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev, return -EIO; } - ret = sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2); - if (ret) - return ret; - - /* - * Mark this mode as tuned. This is critical for both driver tuning - * (SD_HS via set_ios_post) and framework tuning (UHS_SDR104, MMC_HS_200, - * MMC_HS_400) so that subsequent PHY reconfigurations restore the - * calibrated DLL value instead of overwriting with DT defaults. - * - * For HS400, tuning is performed while the controller is in HS200 mode - * (mmc->selected_mode == MMC_HS_200 and mmc->hs400_tuning == true). - * Record the tuned mode as MMC_HS_400 so the calibrated DLL value is - * preserved across the HS200→HS400 transition. - */ - plat->tuned_mode = mmc->hs400_tuning ? MMC_HS_400 : mmc->selected_mode; - - return 0; + return sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2); } static struct dm_mmc_ops sdhci_cdns_mmc_ops; diff --git a/drivers/mmc/sdhci-cadence.h b/drivers/mmc/sdhci-cadence.h index ea517491860..7101f00b75b 100644 --- a/drivers/mmc/sdhci-cadence.h +++ b/drivers/mmc/sdhci-cadence.h @@ -7,8 +7,6 @@ #ifndef SDHCI_CADENCE_H_ #define SDHCI_CADENCE_H_ -#include <mmc.h> - /* HRS - Host Register Set (specific to Cadence) */ /* PHY access port */ #define SDHCI_CDNS_HRS04 0x10 @@ -62,13 +60,10 @@ struct sdhci_cdns_plat { struct mmc_config cfg; struct mmc mmc; void __iomem *hrs_addr; - enum bus_mode tuned_mode; - u32 tuned_dll_slave_ctrl; }; int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode); int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat); int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val); -u32 sdhci_cdns6_phy_get_dll_slave(struct sdhci_cdns_plat *plat); #endif diff --git a/drivers/mmc/sdhci-cadence6.c b/drivers/mmc/sdhci-cadence6.c index c8b42532e17..ca1086e2359 100644 --- a/drivers/mmc/sdhci-cadence6.c +++ b/drivers/mmc/sdhci-cadence6.c @@ -173,30 +173,6 @@ static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat, u32 addr, u3 writel(val, plat->hrs_addr + SDHCI_CDNS_HRS05); } -static bool sdhci_cdns6_mode_is_tuned(struct sdhci_cdns_plat *plat, u32 mode) -{ - /* - * Check if the given mode has a valid tuned DLL value. - * Only modes that support tuning (driver or framework) can have - * valid tuned values. This prevents the initial state (tuned_mode=0) - * from falsely matching MMC_LEGACY. - */ - if (plat->tuned_mode != mode) - return false; - - switch (mode) { - case SD_HS: /* Driver tuning via set_ios_post */ - case UHS_SDR50: /* Future driver tuning support */ - case UHS_SDR104: /* Framework tuning */ - case MMC_HS_200: /* Framework tuning */ - case MMC_HS_400: /* Framework tuning */ - case MMC_HS_400_ES: /* Framework tuning */ - return true; - default: - return false; - } -} - static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat, bool reset) { void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09; @@ -283,18 +259,7 @@ int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 m sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[0].val); sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[1].val); sdhci_cdns6_write_phy_reg(plat, PHY_DLL_MASTER_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[4].val); - if (sdhci_cdns6_mode_is_tuned(plat, mode)) { - /* - * Use previously saved tuned DLL slave control value. - * Note: 0 is a valid tuned value (e.g., optimal tap at position 0), - * so we check both mode match AND that it's a tunable mode. - */ - sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, - plat->tuned_dll_slave_ctrl); - } else { - sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, - sdhci_cdns6_phy_cfgs[2].val); - } + sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val); /* Switch Off the DLL Reset */ ret = sdhci_cdns6_reset_phy_dll(plat, false); @@ -353,9 +318,6 @@ int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val) sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp); - /* Store tuned DLL slave control value which will be reapplied via set_ios(). */ - plat->tuned_dll_slave_ctrl = tmp; - /* Switch Off the DLL Reset */ ret = sdhci_cdns6_reset_phy_dll(plat, false); if (ret) { @@ -365,8 +327,3 @@ int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val) return 0; } - -u32 sdhci_cdns6_phy_get_dll_slave(struct sdhci_cdns_plat *plat) -{ - return sdhci_cdns6_read_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR); -} |
