summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2025-12-11 08:12:49 -0600
committerTom Rini <[email protected]>2025-12-11 08:12:49 -0600
commit802fbe0a287130c4800510ac6df3850c27606ff0 (patch)
treecd5673e9ce62dfd5b1510d6dd9d1aa27e4609c32
parentdd9851d7e5dcb932734a84f93e8ca7bddbce777c (diff)
parented0e33cec099e3f9e459cef7f66ff91068e2d71c (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.c9
-rw-r--r--drivers/mmc/sdhci-cadence.c98
-rw-r--r--drivers/mmc/sdhci-cadence6.c82
-rw-r--r--include/sdhci.h3
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)