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
This commit is contained in:
Tom Rini
2025-12-11 08:12:49 -06:00
4 changed files with 142 additions and 50 deletions

View File

@@ -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);

View File

@@ -2,6 +2,7 @@
/*
* Copyright (C) 2016 Socionext Inc.
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
* 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;

View File

@@ -2,6 +2,7 @@
/*
* Copyright (C) 2023 Starfive.
* Author: Kuan Lim Lee <kuanlim.lee@starfivetech.com>
* 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;
}

View File

@@ -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)