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"
This commit is contained in:
+4
-3
@@ -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.
|
||||
|
||||
+1
-107
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user