diff options
| author | Tom Rini <[email protected]> | 2025-12-11 08:12:49 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2025-12-11 08:12:49 -0600 |
| commit | 802fbe0a287130c4800510ac6df3850c27606ff0 (patch) | |
| tree | cd5673e9ce62dfd5b1510d6dd9d1aa27e4609c32 | |
| parent | dd9851d7e5dcb932734a84f93e8ca7bddbce777c (diff) | |
| parent | ed0e33cec099e3f9e459cef7f66ff91068e2d71c (diff) | |
Merge tag 'mmc-next-2025-12-11' of https://source.denx.de/u-boot/custodians/u-boot-mmc into next
CI: https://source.denx.de/u-boot/custodians/u-boot-mmc/-/pipelines/28729
- mmc: assign f_max to 0 when max-frequency property not exist
- Improvements and minor fixes for Cadence SDHCI driver
| -rw-r--r-- | drivers/mmc/mmc-uclass.c | 9 | ||||
| -rw-r--r-- | drivers/mmc/sdhci-cadence.c | 98 | ||||
| -rw-r--r-- | drivers/mmc/sdhci-cadence6.c | 82 | ||||
| -rw-r--r-- | include/sdhci.h | 3 |
4 files changed, 142 insertions, 50 deletions
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 2f4dc5bd887..bf0bea93853 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -243,8 +243,13 @@ int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) return -EINVAL; } - /* f_max is obtained from the optional "max-frequency" property */ - dev_read_u32(dev, "max-frequency", &cfg->f_max); + /* + * Maximum frequency is obtained from the optional "max-frequency" property. + * If not specified in device tree, defaults to 0 and sdhci_setup_cfg() + * will set the MMC configuration maximum frequency to the host controller's + * maximum base clock frequency from capabilities register. + */ + cfg->f_max = dev_read_u32_default(dev, "max-frequency", 0); if (dev_read_bool(dev, "cap-sd-highspeed")) cfg->host_caps |= MMC_CAP(SD_HS); diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c index 7d169efa476..c7f88977ef9 100644 --- a/drivers/mmc/sdhci-cadence.c +++ b/drivers/mmc/sdhci-cadence.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2016 Socionext Inc. * Author: Masahiro Yamada <[email protected]> + * Copyright (C) 2025 Altera Corporation <www.altera.com> */ #include <dm.h> @@ -15,6 +16,7 @@ #include <linux/sizes.h> #include <linux/libfdt.h> #include <mmc.h> +#include <reset.h> #include <sdhci.h> #include "sdhci-cadence.h" @@ -83,39 +85,74 @@ static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat, return 0; } +static unsigned int sdhci_cdns_get_hrs06_mode(struct mmc *mmc) +{ + unsigned int mode; + + if (IS_SD(mmc)) { + mode = SDHCI_CDNS_HRS06_MODE_SD; + } else { + switch (mmc->selected_mode) { + case MMC_LEGACY: + mode = SDHCI_CDNS_HRS06_MODE_SD; /* use this for Legacy */ + break; + + case MMC_HS: + case MMC_HS_52: + mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR; + break; + + case UHS_DDR50: + case MMC_DDR_52: + mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR; + break; + + case UHS_SDR104: + case MMC_HS_200: + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; + break; + + case MMC_HS_400: + case MMC_HS_400_ES: + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; + break; + + default: + mode = SDHCI_CDNS_HRS06_MODE_SD; + break; + } + } + return mode; +} + static void sdhci_cdns_set_control_reg(struct sdhci_host *host) { struct mmc *mmc = host->mmc; struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev); - unsigned int clock = mmc->clock; u32 mode, tmp; /* - * REVISIT: - * The mode should be decided by MMC_TIMING_* like Linux, but - * U-Boot does not support timing. Use the clock frequency instead. + * Select HRS06 mode based on card type and selected timing mode. + * For SD cards, always use SD mode (000b) as per Cadence user guide, + * section 12.7 (HRS06), Part Number: IP6061. + * For eMMC, use selected_mode to pick the appropriate mode. */ - if (clock <= 26000000) { - mode = SDHCI_CDNS_HRS06_MODE_SD; /* use this for Legacy */ - } else if (clock <= 52000000) { - if (mmc->ddr_mode) - mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR; - else - mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR; - } else { - if (mmc->ddr_mode) - mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; - else - mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; - } + mode = sdhci_cdns_get_hrs06_mode(mmc); tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS06); tmp &= ~SDHCI_CDNS_HRS06_MODE; tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode); writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06); - if (device_is_compatible(mmc->dev, "cdns,sd6hc")) - sdhci_cdns6_phy_adj(mmc->dev, plat, mode); + /* + * For SD cards, program standard SDHCI Host Control2 UHS/voltage + * registers for UHS-I support. + */ + if (IS_SD(mmc)) + sdhci_set_control_reg(host); + + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_420) + sdhci_cdns6_phy_adj(mmc->dev, plat, mmc->selected_mode); } static const struct sdhci_ops sdhci_cdns_ops = { @@ -125,11 +162,13 @@ static const struct sdhci_ops sdhci_cdns_ops = { static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val) { + struct mmc *mmc = &plat->mmc; + struct sdhci_host *host = dev_get_priv(mmc->dev); void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS06; u32 tmp; int i, ret; - if (device_is_compatible(plat->mmc.dev, "cdns,sd6hc")) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_420) return sdhci_cdns6_set_tune_val(plat, val); if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) @@ -168,16 +207,10 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev, int i; /* - * This handler only implements the eMMC tuning that is specific to - * this controller. The tuning for SD timing should be handled by the - * SDHCI core. + * This function performs the tuning process for both SD and eMMC + * interfaces. It sweeps through all available tuning points, + * sending tuning commands at each step. */ - if (!IS_MMC(mmc)) - return -ENOTSUPP; - - if (WARN_ON(opcode != MMC_CMD_SEND_TUNING_BLOCK_HS200)) - return -EINVAL; - for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) { if (sdhci_cdns_set_tune_val(plat, i) || mmc_send_tuning(mmc, opcode)) { /* bad */ @@ -214,6 +247,7 @@ static int sdhci_cdns_probe(struct udevice *dev) struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct sdhci_cdns_plat *plat = dev_get_plat(dev); struct sdhci_host *host = dev_get_priv(dev); + struct reset_ctl_bulk reset_bulk; fdt_addr_t base; int ret; @@ -225,6 +259,10 @@ static int sdhci_cdns_probe(struct udevice *dev) if (!plat->hrs_addr) return -ENOMEM; + ret = reset_get_bulk(dev, &reset_bulk); + if (!ret) + reset_deassert_bulk(&reset_bulk); + host->name = dev->name; host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE; host->ops = &sdhci_cdns_ops; @@ -247,7 +285,7 @@ static int sdhci_cdns_probe(struct udevice *dev) host->mmc = &plat->mmc; host->mmc->dev = dev; - ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); + ret = sdhci_setup_cfg(&plat->cfg, host, plat->cfg.f_max, 0); if (ret) return ret; diff --git a/drivers/mmc/sdhci-cadence6.c b/drivers/mmc/sdhci-cadence6.c index ead96dc0c91..91a245aa490 100644 --- a/drivers/mmc/sdhci-cadence6.c +++ b/drivers/mmc/sdhci-cadence6.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2023 Starfive. * Author: Kuan Lim Lee <[email protected]> + * Copyright (C) 2025 Altera Corporation <www.altera.com> */ #include <dm.h> @@ -57,7 +58,7 @@ #define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY GENMASK(31, 24) #define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY GENMASK(7, 0) -#define SDHCI_CDNS6_PHY_CFG_NUM 4 +#define SDHCI_CDNS6_PHY_CFG_NUM 5 #define SDHCI_CDNS6_CTRL_CFG_NUM 4 struct sdhci_cdns6_phy_cfg { @@ -72,70 +73,90 @@ struct sdhci_cdns6_ctrl_cfg { static struct sdhci_cdns6_phy_cfg sd_ds_phy_cfgs[] = { { "cdns,phy-dqs-timing-delay-sd-ds", 0x00380004, }, - { "cdns,phy-gate-lpbk_ctrl-delay-sd-ds", 0x01A00040, }, + { "cdns,phy-gate-lpbk-ctrl-delay-sd-ds", 0x01A00040, }, { "cdns,phy-dll-slave-ctrl-sd-ds", 0x00000000, }, { "cdns,phy-dq-timing-delay-sd-ds", 0x00000001, }, + { "cdns,phy-dll-master-ctrl-sd-ds", 0x00800004, }, +}; + +static struct sdhci_cdns6_phy_cfg sd_hs_phy_cfgs[] = { + { "cdns,phy-dqs-timing-delay-sd-hs", 0x00380004, }, + { "cdns,phy-gate-lpbk-ctrl-delay-sd-hs", 0x01A00040, }, + { "cdns,phy-dll-slave-ctrl-sd-hs", 0x00000000, }, + { "cdns,phy-dq-timing-delay-sd-hs", 0x00000001, }, + { "cdns,phy-dll-master-ctrl-sd-hs", 0x00800004, }, }; static struct sdhci_cdns6_phy_cfg emmc_sdr_phy_cfgs[] = { - { "cdns,phy-dqs-timing-delay-semmc-sdr", 0x00380004, }, - { "cdns,phy-gate-lpbk_ctrl-delay-emmc-sdr", 0x01A00040, }, + { "cdns,phy-dqs-timing-delay-emmc-sdr", 0x00380004, }, + { "cdns,phy-gate-lpbk-ctrl-delay-emmc-sdr", 0x01A00040, }, { "cdns,phy-dll-slave-ctrl-emmc-sdr", 0x00000000, }, { "cdns,phy-dq-timing-delay-emmc-sdr", 0x00000001, }, + { "cdns,phy-dll-master-ctrl-emmc-sdr", 0x00800004, }, }; static struct sdhci_cdns6_phy_cfg emmc_ddr_phy_cfgs[] = { { "cdns,phy-dqs-timing-delay-emmc-ddr", 0x00380004, }, - { "cdns,phy-gate-lpbk_ctrl-delay-emmc-ddr", 0x01A00040, }, + { "cdns,phy-gate-lpbk-ctrl-delay-emmc-ddr", 0x01A00040, }, { "cdns,phy-dll-slave-ctrl-emmc-ddr", 0x00000000, }, { "cdns,phy-dq-timing-delay-emmc-ddr", 0x10000001, }, + { "cdns,phy-dll-master-ctrl-emmc-ddr", 0x00800004, }, }; static struct sdhci_cdns6_phy_cfg emmc_hs200_phy_cfgs[] = { { "cdns,phy-dqs-timing-delay-emmc-hs200", 0x00380004, }, - { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs200", 0x01A00040, }, + { "cdns,phy-gate-lpbk-ctrl-delay-emmc-hs200", 0x01A00040, }, { "cdns,phy-dll-slave-ctrl-emmc-hs200", 0x00DADA00, }, { "cdns,phy-dq-timing-delay-emmc-hs200", 0x00000001, }, + { "cdns,phy-dll-master-ctrl-emmc-hs200", 0x00000004, }, }; static struct sdhci_cdns6_phy_cfg emmc_hs400_phy_cfgs[] = { { "cdns,phy-dqs-timing-delay-emmc-hs400", 0x00280004, }, - { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs400", 0x01A00040, }, + { "cdns,phy-gate-lpbk-ctrl-delay-emmc-hs400", 0x01A00040, }, { "cdns,phy-dll-slave-ctrl-emmc-hs400", 0x00DAD800, }, { "cdns,phy-dq-timing-delay-emmc-hs400", 0x00000001, }, + { "cdns,phy-dll-master-ctrl-emmc-hs400", 0x00000004, }, }; static struct sdhci_cdns6_ctrl_cfg sd_ds_ctrl_cfgs[] = { { "cdns,ctrl-hrs09-timing-delay-sd-ds", 0x0001800C, }, - { "cdns,ctrl-hrs10-lpbk_ctrl-delay-sd-ds", 0x00020000, }, + { "cdns,ctrl-hrs10-lpbk-ctrl-delay-sd-ds", 0x00020000, }, { "cdns,ctrl-hrs16-slave-ctrl-sd-ds", 0x00000000, }, { "cdns,ctrl-hrs07-timing-delay-sd-ds", 0x00080000, }, }; +static struct sdhci_cdns6_ctrl_cfg sd_hs_ctrl_cfgs[] = { + { "cdns,ctrl-hrs09-timing-delay-sd-hs", 0x0001800C, }, + { "cdns,ctrl-hrs10-lpbk-ctrl-delay-sd-hs", 0x00030000, }, + { "cdns,ctrl-hrs16-slave-ctrl-sd-hs", 0x00000000, }, + { "cdns,ctrl-hrs07-timing-delay-sd-hs", 0x00080000, }, +}; + static struct sdhci_cdns6_ctrl_cfg emmc_sdr_ctrl_cfgs[] = { { "cdns,ctrl-hrs09-timing-delay-emmc-sdr", 0x0001800C, }, - { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-sdr", 0x00030000, }, + { "cdns,ctrl-hrs10-lpbk-ctrl-delay-emmc-sdr", 0x00030000, }, { "cdns,ctrl-hrs16-slave-ctrl-emmc-sdr", 0x00000000, }, { "cdns,ctrl-hrs07-timing-delay-emmc-sdr", 0x00080000, }, }; static struct sdhci_cdns6_ctrl_cfg emmc_ddr_ctrl_cfgs[] = { { "cdns,ctrl-hrs09-timing-delay-emmc-ddr", 0x0001800C, }, - { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-ddr", 0x00020000, }, + { "cdns,ctrl-hrs10-lpbk-ctrl-delay-emmc-ddr", 0x00020000, }, { "cdns,ctrl-hrs16-slave-ctrl-emmc-ddr", 0x11000001, }, { "cdns,ctrl-hrs07-timing-delay-emmc-ddr", 0x00090001, }, }; static struct sdhci_cdns6_ctrl_cfg emmc_hs200_ctrl_cfgs[] = { { "cdns,ctrl-hrs09-timing-delay-emmc-hs200", 0x00018000, }, - { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs200", 0x00080000, }, + { "cdns,ctrl-hrs10-lpbk-ctrl-delay-emmc-hs200", 0x00080000, }, { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs200", 0x00000000, }, { "cdns,ctrl-hrs07-timing-delay-emmc-hs200", 0x00090000, }, }; static struct sdhci_cdns6_ctrl_cfg emmc_hs400_ctrl_cfgs[] = { { "cdns,ctrl-hrs09-timing-delay-emmc-hs400", 0x00018000, }, - { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs400", 0x00080000, }, + { "cdns,ctrl-hrs10-lpbk-ctrl-delay-emmc-hs400", 0x00080000, }, { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs400", 0x11000000, }, { "cdns,ctrl-hrs07-timing-delay-emmc-hs400", 0x00080000, }, }; @@ -186,27 +207,39 @@ int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 m int i, ret; switch (mode) { - case SDHCI_CDNS_HRS06_MODE_SD: + case UHS_SDR12: + case MMC_LEGACY: sdhci_cdns6_phy_cfgs = sd_ds_phy_cfgs; sdhci_cdns6_ctrl_cfgs = sd_ds_ctrl_cfgs; break; - case SDHCI_CDNS_HRS06_MODE_MMC_SDR: + case SD_HS: + case UHS_SDR25: + case MMC_HS: + sdhci_cdns6_phy_cfgs = sd_hs_phy_cfgs; + sdhci_cdns6_ctrl_cfgs = sd_hs_ctrl_cfgs; + break; + + case UHS_SDR50: + case MMC_HS_52: sdhci_cdns6_phy_cfgs = emmc_sdr_phy_cfgs; sdhci_cdns6_ctrl_cfgs = emmc_sdr_ctrl_cfgs; break; - case SDHCI_CDNS_HRS06_MODE_MMC_DDR: + case UHS_DDR50: + case MMC_DDR_52: sdhci_cdns6_phy_cfgs = emmc_ddr_phy_cfgs; sdhci_cdns6_ctrl_cfgs = emmc_ddr_ctrl_cfgs; break; - case SDHCI_CDNS_HRS06_MODE_MMC_HS200: + case UHS_SDR104: + case MMC_HS_200: sdhci_cdns6_phy_cfgs = emmc_hs200_phy_cfgs; sdhci_cdns6_ctrl_cfgs = emmc_hs200_ctrl_cfgs; break; - case SDHCI_CDNS_HRS06_MODE_MMC_HS400: + case MMC_HS_400: + case MMC_HS_400_ES: sdhci_cdns6_phy_cfgs = emmc_hs400_phy_cfgs; sdhci_cdns6_ctrl_cfgs = emmc_hs400_ctrl_cfgs; break; @@ -225,6 +258,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); sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val); /* Switch Off the DLL Reset */ @@ -263,12 +297,13 @@ int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 m int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat) { - return sdhci_cdns6_phy_adj(dev, plat, SDHCI_CDNS_HRS06_MODE_SD); + return sdhci_cdns6_phy_adj(dev, plat, MMC_LEGACY); } int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val) { u32 tmp, tuneval; + int ret; tuneval = (val * 256) / SDHCI_CDNS_MAX_TUNING_LOOP; @@ -277,7 +312,18 @@ int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val) PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY); tmp |= FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY, tuneval) | FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY, tuneval); + + /* Switch On the DLL Reset */ + sdhci_cdns6_reset_phy_dll(plat, true); + sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp); + /* Switch Off the DLL Reset */ + ret = sdhci_cdns6_reset_phy_dll(plat, false); + if (ret) { + printf("sdhci_cdns6_reset_phy is not completed\n"); + return ret; + } + return 0; } diff --git a/include/sdhci.h b/include/sdhci.h index d9c0597a0c1..fb847821d58 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -223,6 +223,9 @@ #define SDHCI_SPEC_100 0 #define SDHCI_SPEC_200 1 #define SDHCI_SPEC_300 2 +#define SDHCI_SPEC_400 3 +#define SDHCI_SPEC_410 4 +#define SDHCI_SPEC_420 5 #define SDHCI_GET_VERSION(x) (x->version & SDHCI_SPEC_VER_MASK) |
