From c42ee367fdab51eb9add12e73e8a25306030bd44 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Dec 2017 02:00:06 +0900 Subject: mmc: do not overwrite cfg->f_max if "max-frequency" if missing mmc_of_parse() in U-Boot is a pussy helper; it sets cfg->f_max to 52MHz even if DT does not provide "max-frequency" at all. This can overwrite cfg->f_max that may have been set to a reasonable default. As the DT binding says, "max-frequency" is an optional property. Do nothing if DT does not specify it. This is the behavior of mmc_of_parse() in Linux. Signed-off-by: Masahiro Yamada --- drivers/mmc/mmc-uclass.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 26c6ab7ad1..7910a3e278 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -146,7 +146,8 @@ int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) break; } - cfg->f_max = dev_read_u32_default(dev, "max-frequency", 52000000); + /* f_max is obtained from the optional "max-frequency" property */ + dev_read_u32(dev, "max-frequency", &cfg->f_max); if (dev_read_bool(dev, "cap-sd-highspeed")) cfg->host_caps |= MMC_CAP(SD_HS); -- cgit From 4b28f7bc930347cb99a6ee2f213ce9db34e36697 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Dec 2017 02:00:07 +0900 Subject: mmc: let mmc_of_parse() fail for insane bus-width value You must fix your DT if it specifies insane bus-width, for example, bus-width = <3>; debug() is not displayed in usual configuration, so people will not even notice weirdness. Use dev_err() instead, then let it fail. Signed-off-by: Masahiro Yamada --- drivers/mmc/mmc-uclass.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 7910a3e278..a3536b15ae 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -140,10 +140,8 @@ int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) cfg->host_caps |= MMC_MODE_1BIT; break; default: - debug("warning: %s invalid bus-width property. using 1-bit\n", - dev_read_name(dev)); - cfg->host_caps |= MMC_MODE_1BIT; - break; + dev_err(dev, "Invalid \"bus-width\" value %u!\n", val); + return -EINVAL; } /* f_max is obtained from the optional "max-frequency" property */ -- cgit From be165fbbf18ad3926337fe51a337dca730fa7278 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Dec 2017 02:00:08 +0900 Subject: mmc: sdhci: do not overwrite host_caps in sdhci_setup_cfg() This line overwrites host_cap that has been set by drivers and/or helpers like mmc_of_parse(). Accumulate capabilities flags. Signed-off-by: Masahiro Yamada --- drivers/mmc/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index e2ddf5dccd..243e0e50f5 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -594,7 +594,7 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) cfg->voltages |= host->voltages; - cfg->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; + cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; /* Since Host Controller Version3.0 */ if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { -- cgit From 954a963146c2f4487f5efce0be00e5625bc41e88 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Dec 2017 02:00:09 +0900 Subject: mmc: sdhci-cadence: use bitfield access macros for cleanup This driver is a counterpart from the one in Linux. Follow the clean-up I did in Linux. Signed-off-by: Masahiro Yamada --- drivers/mmc/sdhci-cadence.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c index 72d1c646a2..712b18c93f 100644 --- a/drivers/mmc/sdhci-cadence.c +++ b/drivers/mmc/sdhci-cadence.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -19,15 +20,14 @@ #define SDHCI_CDNS_HRS04_ACK BIT(26) #define SDHCI_CDNS_HRS04_RD BIT(25) #define SDHCI_CDNS_HRS04_WR BIT(24) -#define SDHCI_CDNS_HRS04_RDATA_SHIFT 16 -#define SDHCI_CDNS_HRS04_WDATA_SHIFT 8 -#define SDHCI_CDNS_HRS04_ADDR_SHIFT 0 +#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16) +#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8) +#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0) #define SDHCI_CDNS_HRS06 0x18 /* eMMC control */ #define SDHCI_CDNS_HRS06_TUNE_UP BIT(15) -#define SDHCI_CDNS_HRS06_TUNE_SHIFT 8 -#define SDHCI_CDNS_HRS06_TUNE_MASK 0x3f -#define SDHCI_CDNS_HRS06_MODE_MASK 0x7 +#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8) +#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0) #define SDHCI_CDNS_HRS06_MODE_SD 0x0 #define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2 #define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3 @@ -84,8 +84,8 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat, u32 tmp; int ret; - tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) | - (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT); + tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) | + FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr); writel(tmp, reg); tmp |= SDHCI_CDNS_HRS04_WR; @@ -152,8 +152,8 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host) } tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS06); - tmp &= ~SDHCI_CDNS_HRS06_MODE_MASK; - tmp |= mode; + tmp &= ~SDHCI_CDNS_HRS06_MODE; + tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode); writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06); } -- cgit From 4041bf7f8ae82537ea8731333ef47f0ad3f33d0f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Dec 2017 02:00:10 +0900 Subject: mmc: sdhci-cadence: call mmc_of_parse() This is needed to parse more capabilities such as mmc-hs200-1_8v. Signed-off-by: Masahiro Yamada --- drivers/mmc/sdhci-cadence.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c index 712b18c93f..921095b276 100644 --- a/drivers/mmc/sdhci-cadence.c +++ b/drivers/mmc/sdhci-cadence.c @@ -190,6 +190,10 @@ static int sdhci_cdns_probe(struct udevice *dev) host->ops = &sdhci_cdns_ops; host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD; + ret = mmc_of_parse(dev, &plat->cfg); + if (ret) + return ret; + ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev)); if (ret) return ret; -- cgit From dd43e2a6bdfa2d21bb2cef07a93c43f819759888 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 12 Jan 2018 18:10:38 +0900 Subject: mmc: sdhci-cadence: add HS200 support Add HS200 timing setting and the MMC tuning callback. Signed-off-by: Masahiro Yamada Signed-off-by: Jaehoon Chung --- drivers/mmc/sdhci-cadence.c | 90 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c index 921095b276..0b174fc44d 100644 --- a/drivers/mmc/sdhci-cadence.c +++ b/drivers/mmc/sdhci-cadence.c @@ -52,6 +52,13 @@ #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d +/* + * The tuned val register is 6 bit-wide, but not the whole of the range is + * available. The range 0-42 seems to be available (then 43 wraps around to 0) + * but I am not quite sure if it is official. Use only 0 to 39 for safety. + */ +#define SDHCI_CDNS_MAX_TUNING_LOOP 40 + struct sdhci_cdns_plat { struct mmc_config cfg; struct mmc mmc; @@ -135,20 +142,18 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host) * The mode should be decided by MMC_TIMING_* like Linux, but * U-Boot does not support timing. Use the clock frequency instead. */ - if (clock <= 26000000) + if (clock <= 26000000) { mode = SDHCI_CDNS_HRS06_MODE_SD; /* use this for Legacy */ - else if (clock <= 52000000) { + } else if (clock <= 52000000) { if (mmc->ddr_mode) mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR; else mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR; } else { - /* - * REVISIT: - * The IP supports HS200/HS400, revisit once U-Boot support it - */ - printf("unsupported frequency %d\n", clock); - return; + if (mmc->ddr_mode) + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; + else + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; } tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS06); @@ -161,6 +166,69 @@ static const struct sdhci_ops sdhci_cdns_ops = { .set_control_reg = sdhci_cdns_set_control_reg, }; +static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat, + unsigned int val) +{ + void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS06; + u32 tmp; + + if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) + return -EINVAL; + + tmp = readl(reg); + tmp &= ~SDHCI_CDNS_HRS06_TUNE; + tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val); + tmp |= SDHCI_CDNS_HRS06_TUNE_UP; + writel(tmp, reg); + + return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), + 1); +} + +static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev, + unsigned int opcode) +{ + struct sdhci_cdns_plat *plat = dev_get_platdata(dev); + struct mmc *mmc = &plat->mmc; + int cur_streak = 0; + int max_streak = 0; + int end_of_streak = 0; + 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. + */ + 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, NULL)) { /* bad */ + cur_streak = 0; + } else { /* good */ + cur_streak++; + if (cur_streak > max_streak) { + max_streak = cur_streak; + end_of_streak = i; + } + } + } + + if (!max_streak) { + dev_err(dev, "no tuning point found\n"); + return -EIO; + } + + return sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2); +} + +static struct dm_mmc_ops sdhci_cdns_mmc_ops; + static int sdhci_cdns_bind(struct udevice *dev) { struct sdhci_cdns_plat *plat = dev_get_platdata(dev); @@ -189,6 +257,10 @@ static int sdhci_cdns_probe(struct udevice *dev) host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE; host->ops = &sdhci_cdns_ops; host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD; + sdhci_cdns_mmc_ops = sdhci_ops; +#ifdef MMC_SUPPORTS_TUNING + sdhci_cdns_mmc_ops.execute_tuning = sdhci_cdns_execute_tuning; +#endif ret = mmc_of_parse(dev, &plat->cfg); if (ret) @@ -223,5 +295,5 @@ U_BOOT_DRIVER(sdhci_cdns) = { .probe = sdhci_cdns_probe, .priv_auto_alloc_size = sizeof(struct sdhci_host), .platdata_auto_alloc_size = sizeof(struct sdhci_cdns_plat), - .ops = &sdhci_ops, + .ops = &sdhci_cdns_mmc_ops, }; -- cgit From 61f2e5ee12895a2bdaeac8dd13e8d7f50ca7e375 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Dec 2017 02:00:12 +0900 Subject: mmc: sdhci: change data transfer failure into debug message During the tuning, drivers repeat data transfer, changing timing parameters in the controller hardware. So, the tuning commands (CMD19 for SD, CMD21 for eMMC) fail, and this is not a problem at all. Showing "Error detected..." in normal operation just make users upset. This should not be shown. Signed-off-by: Masahiro Yamada --- drivers/mmc/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 243e0e50f5..d31793a7b7 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -86,8 +86,8 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) { - printf("%s: Error detected in status(0x%X)!\n", - __func__, stat); + pr_debug("%s: Error detected in status(0x%X)!\n", + __func__, stat); return -EIO; } if (!transfer_done && (stat & rdy)) { -- cgit From 9546eb92cb648a8bba0aa9d5930ac751e6e5b9a4 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 17 Jan 2018 19:36:58 +0900 Subject: mmc: fix the wrong disabling clock When power is off, clock is not disabling. Because it's passed to 1, mmc->clock should be set to f_min value. Some drivers can't initialize the eMMC/SD card with current status. This patch is to fix the disabling clock value to 0. Fixes: 2e7410d76ad1 ("mmc: disable the mmc clock during power off") Signed-off-by: Jaehoon Chung Reviewed-by: Jean-Jacques Hiblot Tested-by: Guillaume GARDET Tested-by: Anand Moon --- drivers/mmc/mmc.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 53c819187e..311f51f237 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1501,11 +1501,13 @@ static int mmc_set_ios(struct mmc *mmc) int mmc_set_clock(struct mmc *mmc, uint clock, bool disable) { - if (clock > mmc->cfg->f_max) - clock = mmc->cfg->f_max; + if (!disable && clock != 0) { + if (clock > mmc->cfg->f_max) + clock = mmc->cfg->f_max; - if (clock < mmc->cfg->f_min) - clock = mmc->cfg->f_min; + if (clock < mmc->cfg->f_min) + clock = mmc->cfg->f_min; + } mmc->clock = clock; mmc->clk_disable = disable; @@ -2449,7 +2451,7 @@ static int mmc_power_on(struct mmc *mmc) static int mmc_power_off(struct mmc *mmc) { - mmc_set_clock(mmc, 1, true); + mmc_set_clock(mmc, 0, true); #if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR) if (mmc->vmmc_supply) { int ret = regulator_set_enable(mmc->vmmc_supply, false); -- cgit From b9b4f146c9bbfb31c50fc1378d3ae44215003bac Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Tue, 16 Jan 2018 22:44:18 +0100 Subject: mmc: fsl_esdhc: Fix i.MX53 eSDHCv3 clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 4f425280fa71 ("mmc: fsl_esdhc: Allow all supported prescaler values") made it possible to set SYSCTL.SDCLKFS to 0 in SDR mode on i.MX, thus bypassing the SD clock frequency prescaler, in order to be able to get higher SD clock frequencies in some contexts. However, that commit missed the fact that this value is illegal on the eSDHCv3 instance of the i.MX53. This seems to be the only exception on i.MX, this value being legal even for the eSDHCv2 instances of the i.MX53. Fix this issue by changing the minimum prescaler value for the single instance of the i.MX53 eSDHCv3 controller. Signed-off-by: Benoît Thébaudeau Reviewed-by: Fabio Estevam --- drivers/mmc/fsl_esdhc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 71c62f4233..8d1e2f8a01 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -528,14 +528,19 @@ out: static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock) { + struct fsl_esdhc *regs = priv->esdhc_regs; int div = 1; #ifdef ARCH_MXC +#ifdef CONFIG_MX53 + /* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */ + int pre_div = (regs == (struct fsl_esdhc *)MMC_SDHC3_BASE_ADDR) ? 2 : 1; +#else int pre_div = 1; +#endif #else int pre_div = 2; #endif int ddr_pre_div = mmc->ddr_mode ? 2 : 1; - struct fsl_esdhc *regs = priv->esdhc_regs; int sdhc_clk = priv->sdhc_clk; uint clk; -- cgit From c0fafe64a5a8fbbd48c4d3bed730f45dfa6d85b5 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 23 Jan 2018 14:04:30 +0900 Subject: mmc: fix to assign to correct clock value when clock is enabling When clock is enabling, it's assigned to 0 as mmc->clock. Then it can't initialize any card. Fix to assign to correct clock value as mmc->cfg->f_min or f_max. Fixes: 9546eb92cb6 ("mmc: fix the wrong disabling clock") Signed-off-by: Jaehoon Chung Tested-by: Guillaume GARDET Tested-by: Anand Moon Tested-by: Stephen Warren --- drivers/mmc/mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 311f51f237..2d0e7bb3a2 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1501,7 +1501,7 @@ static int mmc_set_ios(struct mmc *mmc) int mmc_set_clock(struct mmc *mmc, uint clock, bool disable) { - if (!disable && clock != 0) { + if (!disable) { if (clock > mmc->cfg->f_max) clock = mmc->cfg->f_max; -- cgit From 2f516e4aa286eb0203e34ab9be68b08f7a3c44c1 Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Tue, 2 Jan 2018 12:25:57 +0800 Subject: mmc: Poll for broken card detection case Poll for broken card detection case instead of return no card detected. Signed-off-by: Jun Nie --- drivers/mmc/Kconfig | 5 +++++ drivers/mmc/mmc.c | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index ab0627a8af..bc29611d78 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -17,6 +17,11 @@ config MMC_WRITE help Enable write access to MMC and SD Cards +config MMC_BROKEN_CD + bool "Poll for broken card detection case" + help + If card detection feature is broken, just poll to detect. + config DM_MMC bool "Enable MMC controllers using Driver Model" depends on DM diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 2d0e7bb3a2..255310a8e6 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2493,8 +2493,12 @@ int mmc_start_init(struct mmc *mmc) mmc->host_caps = mmc->cfg->host_caps | MMC_CAP(SD_LEGACY) | MMC_CAP(MMC_LEGACY) | MMC_MODE_1BIT; +#if !defined(CONFIG_MMC_BROKEN_CD) /* we pretend there's no card when init is NULL */ no_card = mmc_getcd(mmc) == 0; +#else + no_card = 0; +#endif #if !CONFIG_IS_ENABLED(DM_MMC) no_card = no_card || (mmc->cfg->ops->init == NULL); #endif -- cgit