From d4d64889b05561d3a3cc8741f86f76d577cb6149 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 28 Jan 2018 19:11:42 +0900 Subject: [PATCH 01/28] mmc: use pr_* log functions Use pr_* log functions from Linux. They can be enabled/disabled via CONFIG_LOGLEVEL. Signed-off-by: Masahiro Yamada Reviewed-by: Lukasz Majewski Reviewed-by: Jaehoon Chung --- drivers/mmc/mmc.c | 68 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 255310a8e6..3a2e3b353f 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -213,8 +213,8 @@ static int mmc_select_mode(struct mmc *mmc, enum bus_mode mode) mmc->selected_mode = mode; mmc->tran_speed = mmc_mode2freq(mmc, mode); mmc->ddr_mode = mmc_is_mode_ddr(mode); - debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), - mmc->tran_speed / 1000000); + pr_debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), + mmc->tran_speed / 1000000); return 0; } @@ -457,7 +457,7 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, } if (mmc_set_blocklen(mmc, mmc->read_bl_len)) { - debug("%s: Failed to set blocklen\n", __func__); + pr_debug("%s: Failed to set blocklen\n", __func__); return 0; } @@ -465,7 +465,7 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, cur = (blocks_todo > mmc->cfg->b_max) ? mmc->cfg->b_max : blocks_todo; if (mmc_read_blocks(mmc, dst, start, cur) != cur) { - debug("%s: Failed to read blocks\n", __func__); + pr_debug("%s: Failed to read blocks\n", __func__); return 0; } blocks_todo -= cur; @@ -900,11 +900,11 @@ static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num) forbidden = MMC_CAP(MMC_HS_200); if (MMC_CAP(mmc->selected_mode) & forbidden) { - debug("selected mode (%s) is forbidden for part %d\n", - mmc_mode_name(mmc->selected_mode), part_num); + pr_debug("selected mode (%s) is forbidden for part %d\n", + mmc_mode_name(mmc->selected_mode), part_num); change = true; } else if (mmc->selected_mode != mmc->best_mode) { - debug("selected mode is not optimal\n"); + pr_debug("selected mode is not optimal\n"); change = true; } @@ -1427,7 +1427,7 @@ retry_ssr: mmc->ssr.erase_offset = eo * 1000; } } else { - debug("Invalid Allocation Unit Size.\n"); + pr_debug("Invalid Allocation Unit Size.\n"); } return 0; @@ -1532,18 +1532,18 @@ void mmc_dump_capabilities(const char *text, uint caps) { enum bus_mode mode; - printf("%s: widths [", text); + pr_debug("%s: widths [", text); if (caps & MMC_MODE_8BIT) - printf("8, "); + pr_debug("8, "); if (caps & MMC_MODE_4BIT) - printf("4, "); + pr_debug("4, "); if (caps & MMC_MODE_1BIT) - printf("1, "); - printf("\b\b] modes ["); + pr_debug("1, "); + pr_debug("\b\b] modes ["); for (mode = MMC_LEGACY; mode < MMC_MODES_END; mode++) if (MMC_CAP(mode) & caps) - printf("%s, ", mmc_mode_name(mode)); - printf("\b\b]\n"); + pr_debug("%s, ", mmc_mode_name(mode)); + pr_debug("\b\b]\n"); } #endif @@ -1577,7 +1577,7 @@ static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) mmc->signal_voltage = signal_voltage; err = mmc_set_ios(mmc); if (err) - debug("unable to set voltage (err %d)\n", err); + pr_debug("unable to set voltage (err %d)\n", err); return err; } @@ -1660,10 +1660,10 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) for (w = widths; w < widths + ARRAY_SIZE(widths); w++) { if (*w & caps & mwt->widths) { - debug("trying mode %s width %d (at %d MHz)\n", - mmc_mode_name(mwt->mode), - bus_width(*w), - mmc_mode2freq(mmc, mwt->mode) / 1000000); + pr_debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(*w), + mmc_mode2freq(mmc, mwt->mode) / 1000000); /* configure the bus width (card + host) */ err = sd_select_bus_width(mmc, bus_width(*w)); @@ -1686,7 +1686,7 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) err = mmc_execute_tuning(mmc, mwt->tuning); if (err) { - debug("tuning failed\n"); + pr_debug("tuning failed\n"); goto error; } } @@ -1708,7 +1708,7 @@ error: } } - printf("unable to select a mode\n"); + pr_err("unable to select a mode\n"); return -ENOTSUPP; } @@ -1860,7 +1860,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) return 0; if (!mmc->ext_csd) { - debug("No ext_csd found!\n"); /* this should enver happen */ + pr_debug("No ext_csd found!\n"); /* this should enver happen */ return -ENOTSUPP; } @@ -1870,10 +1870,10 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) for_each_supported_width(card_caps & mwt->widths, mmc_is_mode_ddr(mwt->mode), ecbw) { enum mmc_voltage old_voltage; - debug("trying mode %s width %d (at %d MHz)\n", - mmc_mode_name(mwt->mode), - bus_width(ecbw->cap), - mmc_mode2freq(mmc, mwt->mode) / 1000000); + pr_debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(ecbw->cap), + mmc_mode2freq(mmc, mwt->mode) / 1000000); old_voltage = mmc->signal_voltage; err = mmc_set_lowest_voltage(mmc, mwt->mode, MMC_ALL_SIGNAL_VOLTAGE); @@ -1914,7 +1914,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) if (mwt->tuning) { err = mmc_execute_tuning(mmc, mwt->tuning); if (err) { - debug("tuning failed\n"); + pr_debug("tuning failed\n"); goto error; } } @@ -2396,12 +2396,12 @@ static int mmc_power_init(struct mmc *mmc) ret = device_get_supply_regulator(mmc->dev, "vmmc-supply", &mmc->vmmc_supply); if (ret) - debug("%s: No vmmc supply\n", mmc->dev->name); + pr_debug("%s: No vmmc supply\n", mmc->dev->name); ret = device_get_supply_regulator(mmc->dev, "vqmmc-supply", &mmc->vqmmc_supply); if (ret) - debug("%s: No vqmmc supply\n", mmc->dev->name); + pr_debug("%s: No vqmmc supply\n", mmc->dev->name); #endif #else /* !CONFIG_DM_MMC */ /* @@ -2457,7 +2457,7 @@ static int mmc_power_off(struct mmc *mmc) int ret = regulator_set_enable(mmc->vmmc_supply, false); if (ret) { - debug("Error disabling VMMC supply\n"); + pr_debug("Error disabling VMMC supply\n"); return ret; } } @@ -2505,7 +2505,7 @@ int mmc_start_init(struct mmc *mmc) if (no_card) { mmc->has_init = 0; #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("MMC: no card present\n"); + pr_err("MMC: no card present\n"); #endif return -ENOMEDIUM; } @@ -2532,7 +2532,7 @@ int mmc_start_init(struct mmc *mmc) * to use the UHS modes, because we wouldn't be able to * recover from an error during the UHS initialization. */ - debug("Unable to do a full power cycle. Disabling the UHS modes for safety\n"); + pr_debug("Unable to do a full power cycle. Disabling the UHS modes for safety\n"); uhs_en = false; mmc->host_caps &= ~UHS_CAPS; err = mmc_power_on(mmc); @@ -2629,7 +2629,7 @@ int mmc_init(struct mmc *mmc) if (!err) err = mmc_complete_init(mmc); if (err) - printf("%s: %d, time %lu\n", __func__, err, get_timer(start)); + pr_info("%s: %d, time %lu\n", __func__, err, get_timer(start)); return err; } From 5baf543e5277ec30c2e9fdb3e89dafc926532bac Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:30 +0100 Subject: [PATCH 02/28] mmc: omap_hsmmc: cleanup clock configuration Add a separate function for starting the clock, stopping the clock and setting the clock. Starting the clock and stopping the clock can be used irrespective of setting the clock (For example during iodelay recalibration). Also set the clock only if there is a change in frequency. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 2 + drivers/mmc/omap_hsmmc.c | 76 ++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index bf9de9b211..102aec28f9 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -172,6 +172,8 @@ struct omap_hsmmc_plat { #define CLK_400KHZ 1 #define CLK_MISC 2 +#define CLKD_MAX 0x3FF /* max clock divisor: 1023 */ + #define RSP_TYPE_NONE (RSP_TYPE_NORSP | CCCE_NOCHECK | CICE_NOCHECK) #define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index b12d6d9102..fa8681ab69 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -62,6 +62,7 @@ struct omap_hsmmc_data { #if !CONFIG_IS_ENABLED(DM_MMC) struct mmc_config cfg; #endif + uint clock; #ifdef OMAP_HSMMC_USE_GPIO #if CONFIG_IS_ENABLED(DM_MMC) struct gpio_desc cd_gpio; /* Change Detect GPIO */ @@ -114,6 +115,8 @@ struct omap_hsmmc_adma_desc { static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int siz); +static void omap_hsmmc_start_clock(struct hsmmc *mmc_base); +static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base); static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc) { @@ -764,6 +767,53 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, return 0; } +static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base) +{ + writel(readl(&mmc_base->sysctl) & ~CEN_ENABLE, &mmc_base->sysctl); +} + +static void omap_hsmmc_start_clock(struct hsmmc *mmc_base) +{ + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); +} + +static void omap_hsmmc_set_clock(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base; + unsigned int dsor = 0; + ulong start; + + mmc_base = priv->base_addr; + omap_hsmmc_stop_clock(mmc_base); + + /* TODO: Is setting DTO required here? */ + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK), + (ICE_STOP | DTO_15THDTO)); + + if (mmc->clock != 0) { + dsor = DIV_ROUND_UP(MMC_CLOCK_REFERENCE * 1000000, mmc->clock); + if (dsor > CLKD_MAX) + dsor = CLKD_MAX; + } else { + dsor = CLKD_MAX; + } + + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for ics!\n", __func__); + return; + } + } + + priv->clock = mmc->clock; + omap_hsmmc_start_clock(mmc_base); +} + #if !CONFIG_IS_ENABLED(DM_MMC) static int omap_hsmmc_set_ios(struct mmc *mmc) { @@ -776,8 +826,6 @@ static int omap_hsmmc_set_ios(struct udevice *dev) struct mmc *mmc = upriv->mmc; #endif struct hsmmc *mmc_base; - unsigned int dsor = 0; - ulong start; mmc_base = priv->base_addr; /* configue bus width */ @@ -803,28 +851,8 @@ static int omap_hsmmc_set_ios(struct udevice *dev) break; } - /* configure clock with 96Mhz system clock. - */ - if (mmc->clock != 0) { - dsor = (MMC_CLOCK_REFERENCE * 1000000 / mmc->clock); - if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > mmc->clock) - dsor++; - } - - mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), - (ICE_STOP | DTO_15THDTO)); - - mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, - (dsor << CLKD_OFFSET) | ICE_OSCILLATE); - - start = get_timer(0); - while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { - if (get_timer(0) - start > MAX_RETRY_MS) { - printf("%s: timedout waiting for ics!\n", __func__); - return -ETIMEDOUT; - } - } - writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); + if (priv->clock != mmc->clock) + omap_hsmmc_set_clock(mmc); return 0; } From 48a2f11443a1f15c1dcee4f2051f12edc6378510 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:31 +0100 Subject: [PATCH 03/28] mmc: omap_hsmmc: cleanup omap_hsmmc_set_ios No functional change. Move bus width configuration setting to a separate function and invoke it only if there is a change in the bus width. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index fa8681ab69..713faab110 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -62,6 +62,7 @@ struct omap_hsmmc_data { #if !CONFIG_IS_ENABLED(DM_MMC) struct mmc_config cfg; #endif + uint bus_width; uint clock; #ifdef OMAP_HSMMC_USE_GPIO #if CONFIG_IS_ENABLED(DM_MMC) @@ -814,17 +815,9 @@ static void omap_hsmmc_set_clock(struct mmc *mmc) omap_hsmmc_start_clock(mmc_base); } -#if !CONFIG_IS_ENABLED(DM_MMC) -static int omap_hsmmc_set_ios(struct mmc *mmc) +static void omap_hsmmc_set_bus_width(struct mmc *mmc) { struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); -#else -static int omap_hsmmc_set_ios(struct udevice *dev) -{ - struct omap_hsmmc_data *priv = dev_get_priv(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - struct mmc *mmc = upriv->mmc; -#endif struct hsmmc *mmc_base; mmc_base = priv->base_addr; @@ -851,6 +844,24 @@ static int omap_hsmmc_set_ios(struct udevice *dev) break; } + priv->bus_width = mmc->bus_width; +} + +#if !CONFIG_IS_ENABLED(DM_MMC) +static int omap_hsmmc_set_ios(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); +#else +static int omap_hsmmc_set_ios(struct udevice *dev) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; +#endif + + if (priv->bus_width != mmc->bus_width) + omap_hsmmc_set_bus_width(mmc); + if (priv->clock != mmc->clock) omap_hsmmc_set_clock(mmc); From b59448170934094570a497ff7c5b4b2e94cff5a2 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:32 +0100 Subject: [PATCH 04/28] mmc: omap_hsmmc: add support to set default io voltage "ti,dual-volt" is used in linux kernel to set the voltage capabilities. For host controller dt nodes that doesn't have "ti,dual-volt", it's assumed 1.8v is the io voltage. This is not always true (like in the case of beagle-x15 where the io lines are connected to 3.3v). Hence if "no-1-8-v" property is set, io voltage will be set to 3v. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 12 +++++- drivers/mmc/omap_hsmmc.c | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 102aec28f9..c4d326dfc1 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -65,6 +65,7 @@ struct omap_hsmmc_plat { struct hsmmc *base_addr; struct mmc mmc; bool cd_inverted; + u32 controller_flags; }; /* @@ -124,8 +125,10 @@ struct omap_hsmmc_plat { #define DTW_8_BITMODE (0x1 << 5) /* CON[DW8]*/ #define SDBP_PWROFF (0x0 << 8) #define SDBP_PWRON (0x1 << 8) +#define SDVS_MASK (0x7 << 9) #define SDVS_1V8 (0x5 << 9) #define SDVS_3V0 (0x6 << 9) +#define SDVS_3V3 (0x7 << 9) #define DMA_SELECT (0x2 << 3) #define ICE_MASK (0x1 << 0) #define ICE_STOP (0x0 << 0) @@ -159,8 +162,13 @@ struct omap_hsmmc_plat { #define IE_CERR (0x01 << 28) #define IE_BADA (0x01 << 29) -#define VS30_3V0SUP (1 << 25) -#define VS18_1V8SUP (1 << 26) +#define VS33_3V3SUP BIT(24) +#define VS30_3V0SUP BIT(25) +#define VS18_1V8SUP BIT(26) + +#define IOV_3V3 3300000 +#define IOV_3V0 3000000 +#define IOV_1V8 1800000 /* Driver definitions */ #define MMCSD_SECTOR_SIZE 512 diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 713faab110..5141bf66e1 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -73,6 +73,9 @@ struct omap_hsmmc_data { int cd_gpio; int wp_gpio; #endif +#endif +#if CONFIG_IS_ENABLED(DM_MMC) + uint iov; #endif u8 controller_flags; #ifndef CONFIG_OMAP34XX @@ -111,6 +114,8 @@ struct omap_hsmmc_adma_desc { * that the bandwidth is always above 3MB/s). */ #define DMA_TIMEOUT_PER_MB 333 +#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) +#define OMAP_HSMMC_NO_1_8_V BIT(1) #define OMAP_HSMMC_USE_ADMA BIT(2) static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); @@ -252,6 +257,58 @@ void mmc_init_stream(struct hsmmc *mmc_base) writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con); } +#if CONFIG_IS_ENABLED(DM_MMC) +static void omap_hsmmc_conf_bus_power(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + + val = readl(&mmc_base->hctl) & ~SDVS_MASK; + + switch (priv->iov) { + case IOV_3V3: + val |= SDVS_3V3; + break; + case IOV_3V0: + val |= SDVS_3V0; + break; + case IOV_1V8: + val |= SDVS_1V8; + break; + } + + writel(val, &mmc_base->hctl); +} + +static void omap_hsmmc_set_capabilities(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->capa); + + if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { + val |= (VS30_3V0SUP | VS18_1V8SUP); + priv->iov = IOV_3V0; + } else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) { + val |= VS30_3V0SUP; + val &= ~VS18_1V8SUP; + priv->iov = IOV_3V0; + } else { + val |= VS18_1V8SUP; + val &= ~VS30_3V0SUP; + priv->iov = IOV_1V8; + } + + writel(val, &mmc_base->capa); +} +#endif + static int omap_hsmmc_init_setup(struct mmc *mmc) { struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -286,9 +343,15 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) if (reg_val & MADMA_EN) priv->controller_flags |= OMAP_HSMMC_USE_ADMA; #endif + +#if CONFIG_IS_ENABLED(DM_MMC) + omap_hsmmc_set_capabilities(mmc); + omap_hsmmc_conf_bus_power(mmc); +#else writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, &mmc_base->capa); +#endif reg_val = readl(&mmc_base->con) & RESERVED_MASK; @@ -1071,6 +1134,10 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000); cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + if (fdtdec_get_bool(fdt, node, "ti,dual-volt")) + plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; + if (fdtdec_get_bool(fdt, node, "no-1-8-v")) + plat->controller_flags |= OMAP_HSMMC_NO_1_8_V; #ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted"); From 8fc238bfada16d61dee4240de2d49f38a6a6f941 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:33 +0100 Subject: [PATCH 05/28] mmc: omap_hsmmc: set MMC mode in the UHSMS bit field Use the timing parameter set in the MMC core to set the mode in UHSMS bit field. This is in preparation for adding HS200 support in omap hsmmc driver. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 12 ++++++++- drivers/mmc/omap_hsmmc.c | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index c4d326dfc1..507435a11f 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -53,7 +53,8 @@ struct hsmmc { unsigned int sysctl; /* 0x12C */ unsigned int stat; /* 0x130 */ unsigned int ie; /* 0x134 */ - unsigned char res4[0x8]; + unsigned char res4[0x4]; + unsigned int ac12; /* 0x13C */ unsigned int capa; /* 0x140 */ unsigned char res5[0x10]; unsigned int admaes; /* 0x154 */ @@ -170,6 +171,15 @@ struct omap_hsmmc_plat { #define IOV_3V0 3000000 #define IOV_1V8 1800000 +#define AC12_ET BIT(22) +#define AC12_UHSMC_MASK (7 << 16) +#define AC12_UHSMC_DDR50 (4 << 16) +#define AC12_UHSMC_SDR104 (3 << 16) +#define AC12_UHSMC_SDR50 (2 << 16) +#define AC12_UHSMC_SDR25 (1 << 16) +#define AC12_UHSMC_SDR12 (0 << 16) +#define AC12_UHSMC_RES (0x7 << 16) + /* Driver definitions */ #define MMCSD_SECTOR_SIZE 512 #define MMC_CARD 0 diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 5141bf66e1..c6b74a1263 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -76,6 +76,7 @@ struct omap_hsmmc_data { #endif #if CONFIG_IS_ENABLED(DM_MMC) uint iov; + enum bus_mode mode; #endif u8 controller_flags; #ifndef CONFIG_OMAP34XX @@ -258,6 +259,48 @@ void mmc_init_stream(struct hsmmc *mmc_base) } #if CONFIG_IS_ENABLED(DM_MMC) +static void omap_hsmmc_set_timing(struct mmc *mmc) +{ + u32 val; + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + + mmc_base = priv->base_addr; + + val = readl(&mmc_base->ac12); + val &= ~AC12_UHSMC_MASK; + priv->mode = mmc->selected_mode; + + switch (priv->mode) { + case MMC_HS_200: + case UHS_SDR104: + val |= AC12_UHSMC_SDR104; + break; + case UHS_SDR50: + val |= AC12_UHSMC_SDR50; + break; + case MMC_DDR_52: + case UHS_DDR50: + val |= AC12_UHSMC_DDR50; + break; + case SD_HS: + case MMC_HS_52: + case UHS_SDR25: + val |= AC12_UHSMC_SDR25; + break; + case MMC_LEGACY: + case MMC_HS: + case SD_LEGACY: + case UHS_SDR12: + val |= AC12_UHSMC_SDR12; + break; + default: + val |= AC12_UHSMC_RES; + break; + } + writel(val, &mmc_base->ac12); +} + static void omap_hsmmc_conf_bus_power(struct mmc *mmc) { struct hsmmc *mmc_base; @@ -928,6 +971,10 @@ static int omap_hsmmc_set_ios(struct udevice *dev) if (priv->clock != mmc->clock) omap_hsmmc_set_clock(mmc); +#if CONFIG_IS_ENABLED(DM_MMC) + if (priv->mode != mmc->selected_mode) + omap_hsmmc_set_timing(mmc); +#endif return 0; } From 9b3fc21837dc32eda9656f264f74719ea77311a2 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:34 +0100 Subject: [PATCH 06/28] mmc: omap_hsmmc: Enable DDR mode support In order to enable DDR mode, Dual Data Rate mode bit has to be set in MMCHS_CON register. Set it here. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 1 + drivers/mmc/omap_hsmmc.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 507435a11f..6aca9e90cf 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -89,6 +89,7 @@ struct omap_hsmmc_plat { #define WPP_ACTIVEHIGH (0x0 << 8) #define RESERVED_MASK (0x3 << 9) #define CTPL_MMC_SD (0x0 << 11) +#define DDR (0x1 << 19) #define DMA_MASTER (0x1 << 20) #define BLEN_512BYTESLEN (0x200 << 0) #define NBLK_STPCNT (0x0 << 16) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index c6b74a1263..2f4909e34b 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -271,6 +271,11 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) val &= ~AC12_UHSMC_MASK; priv->mode = mmc->selected_mode; + if (mmc_is_mode_ddr(priv->mode)) + writel(readl(&mmc_base->con) | DDR, &mmc_base->con); + else + writel(readl(&mmc_base->con) & ~DDR, &mmc_base->con); + switch (priv->mode) { case MMC_HS_200: case UHS_SDR104: From 14761caeee3ba453d051c0db3246fbccc5a5b136 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:35 +0100 Subject: [PATCH 07/28] mmc: omap_hsmmc: Add tuning support HS200/SDR104 requires tuning command to be sent to the card. Use the mmc_send_tuning library function to send the tuning command and configure the internal DLL. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 21 +++++- drivers/mmc/omap_hsmmc.c | 122 ++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 6aca9e90cf..cf9f1c54ff 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -39,7 +39,9 @@ struct hsmmc { unsigned int sysstatus; /* 0x14 */ unsigned char res2[0x14]; unsigned int con; /* 0x2C */ - unsigned char res3[0xD4]; + unsigned int pwcnt; /* 0x30 */ + unsigned int dll; /* 0x34 */ + unsigned char res3[0xcc]; unsigned int blk; /* 0x104 */ unsigned int arg; /* 0x108 */ unsigned int cmd; /* 0x10C */ @@ -56,7 +58,8 @@ struct hsmmc { unsigned char res4[0x4]; unsigned int ac12; /* 0x13C */ unsigned int capa; /* 0x140 */ - unsigned char res5[0x10]; + unsigned int capa2; /* 0x144 */ + unsigned char res5[0xc]; unsigned int admaes; /* 0x154 */ unsigned int admasal; /* 0x158 */ }; @@ -173,6 +176,8 @@ struct omap_hsmmc_plat { #define IOV_1V8 1800000 #define AC12_ET BIT(22) +#define AC12_V1V8_SIGEN BIT(19) +#define AC12_SCLK_SEL BIT(23) #define AC12_UHSMC_MASK (7 << 16) #define AC12_UHSMC_DDR50 (4 << 16) #define AC12_UHSMC_SDR104 (3 << 16) @@ -199,6 +204,18 @@ struct omap_hsmmc_plat { /* Clock Configurations and Macros */ #define MMC_CLOCK_REFERENCE 96 /* MHz */ +/* DLL */ +#define DLL_SWT BIT(20) +#define DLL_FORCE_SR_C_SHIFT 13 +#define DLL_FORCE_SR_C_MASK 0x7f +#define DLL_FORCE_VALUE BIT(12) +#define DLL_CALIB BIT(1) + +#define MAX_PHASE_DELAY 0x7c + +/* CAPA2 */ +#define CAPA2_TSDR50 BIT(13) + #define mmc_reg_out(addr, mask, val)\ writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr)) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2f4909e34b..69a7c2eed4 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -124,6 +124,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int siz); static void omap_hsmmc_start_clock(struct hsmmc *mmc_base); static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base); +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit); static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc) { @@ -355,6 +356,124 @@ static void omap_hsmmc_set_capabilities(struct mmc *mmc) writel(val, &mmc_base->capa); } + +#ifdef MMC_SUPPORTS_TUNING +static void omap_hsmmc_disable_tuning(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->ac12); + val &= ~(AC12_SCLK_SEL); + writel(val, &mmc_base->ac12); + + val = readl(&mmc_base->dll); + val &= ~(DLL_FORCE_VALUE | DLL_SWT); + writel(val, &mmc_base->dll); +} + +static void omap_hsmmc_set_dll(struct mmc *mmc, int count) +{ + int i; + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->dll); + val |= DLL_FORCE_VALUE; + val &= ~(DLL_FORCE_SR_C_MASK << DLL_FORCE_SR_C_SHIFT); + val |= (count << DLL_FORCE_SR_C_SHIFT); + writel(val, &mmc_base->dll); + + val |= DLL_CALIB; + writel(val, &mmc_base->dll); + for (i = 0; i < 1000; i++) { + if (readl(&mmc_base->dll) & DLL_CALIB) + break; + } + val &= ~DLL_CALIB; + writel(val, &mmc_base->dll); +} + +static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; + struct hsmmc *mmc_base; + u32 val; + u8 cur_match, prev_match = 0; + int ret; + u32 phase_delay = 0; + u32 start_window = 0, max_window = 0; + u32 length = 0, max_len = 0; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->capa2); + + /* clock tuning is not needed for upto 52MHz */ + if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == UHS_SDR104) || + ((mmc->selected_mode == UHS_SDR50) && (val & CAPA2_TSDR50)))) + return 0; + + val = readl(&mmc_base->dll); + val |= DLL_SWT; + writel(val, &mmc_base->dll); + while (phase_delay <= MAX_PHASE_DELAY) { + omap_hsmmc_set_dll(mmc, phase_delay); + + cur_match = !mmc_send_tuning(mmc, opcode, NULL); + + if (cur_match) { + if (prev_match) { + length++; + } else { + start_window = phase_delay; + length = 1; + } + } + + if (length > max_len) { + max_window = start_window; + max_len = length; + } + + prev_match = cur_match; + phase_delay += 4; + } + + if (!max_len) { + ret = -EIO; + goto tuning_error; + } + + val = readl(&mmc_base->ac12); + if (!(val & AC12_SCLK_SEL)) { + ret = -EIO; + goto tuning_error; + } + + phase_delay = max_window + 4 * ((3 * max_len) >> 2); + omap_hsmmc_set_dll(mmc, phase_delay); + + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return 0; + +tuning_error: + + omap_hsmmc_disable_tuning(mmc); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return ret; +} +#endif #endif static int omap_hsmmc_init_setup(struct mmc *mmc) @@ -1050,6 +1169,9 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { .get_cd = omap_hsmmc_getcd, .get_wp = omap_hsmmc_getwp, #endif +#ifdef MMC_SUPPORTS_TUNING + .execute_tuning = omap_hsmmc_execute_tuning, +#endif }; #else static const struct mmc_ops omap_hsmmc_ops = { From 2faa1a302ba13ed65771d642eed126e458e41bf3 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:36 +0100 Subject: [PATCH 08/28] mmc: omap_hsmmc: Workaround for errata id i802 According to errata i802, DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur during the tuning procedure. The DCRC interrupt, occurs when the last tuning block fails (the last ratio tested). The delay from CRC check until the interrupt is asserted is bigger than the delay until assertion of the tuning end flag. Assertion of tuning end flag is what masks the interrupts. Because of this race, an erroneous DCRC interrupt occurs. The suggested workaround is to disable DCRC interrupts during the tuning procedure which is implemented here. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 4 ++++ drivers/mmc/omap_hsmmc.c | 28 ++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index cf9f1c54ff..635ce1e488 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -219,6 +219,10 @@ struct omap_hsmmc_plat { #define mmc_reg_out(addr, mask, val)\ writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr)) +#define INT_EN_MASK (IE_BADA | IE_CERR | IE_DEB | IE_DCRC |\ + IE_DTO | IE_CIE | IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO |\ + IE_BRR | IE_BWR | IE_TC | IE_CC) + int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, int wp_gpio); diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 69a7c2eed4..55232103e0 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -476,6 +476,25 @@ tuning_error: #endif #endif +static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base = priv->base_addr; + u32 irq_mask = INT_EN_MASK; + + /* + * TODO: Errata i802 indicates only DCRC interrupts can occur during + * tuning procedure and DCRC should be disabled. But see occurences + * of DEB, CIE, CEB, CCRC interupts during tuning procedure. These + * interrupts occur along with BRR, so the data is actually in the + * buffer. It has to be debugged why these interrutps occur + */ + if (cmd && mmc_is_tuning_cmd(cmd->cmdidx)) + irq_mask &= ~(IE_DEB | IE_DCRC | IE_CIE | IE_CEB | IE_CCRC); + + writel(irq_mask, &mmc_base->ie); +} + static int omap_hsmmc_init_setup(struct mmc *mmc) { struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -542,10 +561,7 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl); - writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE | - IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC | - IE_CC, &mmc_base->ie); - + mmc_enable_irq(mmc, NULL); mmc_init_stream(mmc_base); return 0; @@ -718,10 +734,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct omap_hsmmc_data *priv = dev_get_priv(dev); -#ifndef CONFIG_OMAP34XX struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = upriv->mmc; -#endif #endif struct hsmmc *mmc_base; unsigned int flags, mmc_stat; @@ -810,6 +824,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, #endif } + mmc_enable_irq(mmc, cmd); + writel(cmd->cmdarg, &mmc_base->arg); udelay(20); /* To fix "No status update" error on eMMC */ writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd); From a4efd73773c792737dd8d1e9d18da7796418fc1f Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:37 +0100 Subject: [PATCH 09/28] mmc: omap_hsmmc: Reduce the max timeout for reset controller fsm >From OMAP3 SoCs (OMAP3, OMAP4, OMAP5, AM572x, AM571x), the DAT/CMD lines reset procedure section in TRM suggests to first poll the SRD/SRC bit until it is set to 0x1. But looks like that bit is never set to 1 and there is an observable delay of 1sec everytime the driver tries to reset DAT/CMD. (The same is observed in linux kernel). Reduce the time the driver waits for the controller to set the SRC/SRD bits to 1 so that there is no observable delay. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 55232103e0..ab4a095233 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -108,6 +108,7 @@ struct omap_hsmmc_adma_desc { /* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 +#define MMC_TIMEOUT_MS 20 /* DMA transfers can take a long time if a lot a data is transferred. * The timeout must take in account the amount of data. Let's assume @@ -598,7 +599,7 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) if (!(readl(&mmc_base->sysctl) & bit)) { start = get_timer(0); while (!(readl(&mmc_base->sysctl) & bit)) { - if (get_timer(0) - start > MAX_RETRY_MS) + if (get_timer(0) - start > MMC_TIMEOUT_MS) return; } } From 2d7482cf793fe4d8a98906002708d6e1fa2c5ba3 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:38 +0100 Subject: [PATCH 10/28] mmc: omap_hsmmc: use mmc_of_parse to populate mmc_config Use the mmc_of_parse library function to populate mmc_config instead of repeating the same code in host controller driver. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index ab4a095233..57548ee31f 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -1297,32 +1297,18 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) struct mmc_config *cfg = &plat->cfg; const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); - int val; + int ret; plat->base_addr = map_physmem(devfdt_get_addr(dev), sizeof(struct hsmmc *), MAP_NOCACHE); - cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - val = fdtdec_get_int(fdt, node, "bus-width", -1); - if (val < 0) { - printf("error: bus-width property missing\n"); - return -ENOENT; - } - - switch (val) { - case 0x8: - cfg->host_caps |= MMC_MODE_8BIT; - case 0x4: - cfg->host_caps |= MMC_MODE_4BIT; - break; - default: - printf("error: invalid bus-width property\n"); - return -ENOENT; - } + ret = mmc_of_parse(dev, cfg); + if (ret < 0) + return ret; + cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->f_min = 400000; - cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000); cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; if (fdtdec_get_bool(fdt, node, "ti,dual-volt")) From 6a27333ba36faea7fdfc3fa7ffe8b947f9cb8912 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:39 +0100 Subject: [PATCH 11/28] ARM: OMAP5/DRA7: Enable iodelay recalibration to be done from uboot Add a new API to perform iodelay recalibration without isolate io to be used in uboot. The data manual of J6/J6 Eco recommends to set different IODELAY values depending on the mode in which the MMC/SD is enumerated in order to ensure IO timings are met. The MMC driver can use the new API to set the IO delay values depending on the MMC mode. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- .../include/asm/arch-omap5/dra7xx_iodelay.h | 3 ++ arch/arm/mach-omap2/omap5/dra7xx_iodelay.c | 30 +++++++++++++++++++ include/configs/am57xx_evm.h | 2 -- include/configs/dra7xx_evm.h | 2 -- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h index c997004039..a8780eeef8 100644 --- a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h +++ b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h @@ -83,6 +83,9 @@ void __recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, struct iodelay_cfg_entry const *iodelay, int niodelays); +void late_recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, + struct iodelay_cfg_entry const *iodelay, + int niodelays); int __recalibrate_iodelay_start(void); void __recalibrate_iodelay_end(int ret); diff --git a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c index 87987308ac..a9a9f75e6e 100644 --- a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c +++ b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c @@ -272,3 +272,33 @@ err: __recalibrate_iodelay_end(ret); } + +void late_recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, + struct iodelay_cfg_entry const *iodelay, + int niodelays) +{ + int ret = 0; + + /* unlock IODELAY CONFIG registers */ + writel(CFG_IODELAY_UNLOCK_KEY, (*ctrl)->iodelay_config_base + + CFG_REG_8_OFFSET); + + ret = calibrate_iodelay((*ctrl)->iodelay_config_base); + if (ret) + goto err; + + ret = update_delay_mechanism((*ctrl)->iodelay_config_base); + + /* Configure Mux settings */ + do_set_mux32((*ctrl)->control_padconf_core_base, pad, npads); + + /* Configure Manual IO timing modes */ + ret = do_set_iodelay((*ctrl)->iodelay_config_base, iodelay, niodelays); + if (ret) + goto err; + +err: + /* lock IODELAY CONFIG registers */ + writel(CFG_IODELAY_LOCK_KEY, (*ctrl)->iodelay_config_base + + CFG_REG_8_OFFSET); +} diff --git a/include/configs/am57xx_evm.h b/include/configs/am57xx_evm.h index 7546b3f9d0..65465d1f79 100644 --- a/include/configs/am57xx_evm.h +++ b/include/configs/am57xx_evm.h @@ -15,9 +15,7 @@ #include #include -#ifdef CONFIG_SPL_BUILD #define CONFIG_IODELAY_RECALIBRATION -#endif #define CONFIG_NR_DRAM_BANKS 2 diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h index ff90b6df72..975e6fdf58 100644 --- a/include/configs/dra7xx_evm.h +++ b/include/configs/dra7xx_evm.h @@ -14,9 +14,7 @@ #include -#ifdef CONFIG_SPL_BUILD #define CONFIG_IODELAY_RECALIBRATION -#endif #define CONFIG_VERY_BIG_RAM #define CONFIG_NR_DRAM_BANKS 2 From 33c1d77f4ad7dc7cc2e449c15a757a679f9237a6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:40 +0100 Subject: [PATCH 12/28] mmc: omap_hsmmc: Add support to set IODELAY values The data manual of J6/J6 Eco recommends to set different IODELAY values depending on the mode in which the MMC/SD is enumerated in order to ensure IO timings are met. Add support to parse mux values and iodelay values from device tree and set these depending on the enumerated MMC mode. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 372 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 57548ee31f..2b77422eee 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -34,6 +34,10 @@ #endif #include #include +#ifdef CONFIG_OMAP54XX +#include +#include +#endif #if !defined(CONFIG_SOC_KEYSTONE) #include #include @@ -57,6 +61,15 @@ DECLARE_GLOBAL_DATA_PTR; #define SYSCTL_SRC (1 << 25) #define SYSCTL_SRD (1 << 26) +#ifdef CONFIG_IODELAY_RECALIBRATION +struct omap_hsmmc_pinctrl_state { + struct pad_conf_entry *padconf; + int npads; + struct iodelay_cfg_entry *iodelay; + int niodelays; +}; +#endif + struct omap_hsmmc_data { struct hsmmc *base_addr; #if !CONFIG_IS_ENABLED(DM_MMC) @@ -83,6 +96,21 @@ struct omap_hsmmc_data { struct omap_hsmmc_adma_desc *adma_desc_table; uint desc_slot; #endif +#ifdef CONFIG_IODELAY_RECALIBRATION + struct omap_hsmmc_pinctrl_state *default_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs200_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr12_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr25_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr104_pinctrl_state; +#endif +}; + +struct omap_mmc_of_data { + u8 controller_flags; }; #ifndef CONFIG_OMAP34XX @@ -119,6 +147,7 @@ struct omap_hsmmc_adma_desc { #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) #define OMAP_HSMMC_NO_1_8_V BIT(1) #define OMAP_HSMMC_USE_ADMA BIT(2) +#define OMAP_HSMMC_REQUIRE_IODELAY BIT(3) static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, @@ -261,6 +290,56 @@ void mmc_init_stream(struct hsmmc *mmc_base) } #if CONFIG_IS_ENABLED(DM_MMC) +#ifdef CONFIG_IODELAY_RECALIBRATION +static void omap_hsmmc_io_recalibrate(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + switch (priv->mode) { + case MMC_HS_200: + pinctrl_state = priv->hs200_1_8v_pinctrl_state; + break; + case UHS_SDR104: + pinctrl_state = priv->sdr104_pinctrl_state; + break; + case UHS_SDR50: + pinctrl_state = priv->sdr50_pinctrl_state; + break; + case UHS_DDR50: + pinctrl_state = priv->ddr50_pinctrl_state; + break; + case UHS_SDR25: + pinctrl_state = priv->sdr25_pinctrl_state; + break; + case UHS_SDR12: + pinctrl_state = priv->sdr12_pinctrl_state; + break; + case SD_HS: + case MMC_HS: + case MMC_HS_52: + pinctrl_state = priv->hs_pinctrl_state; + break; + case MMC_DDR_52: + pinctrl_state = priv->ddr_1_8v_pinctrl_state; + default: + pinctrl_state = priv->default_pinctrl_state; + break; + } + + if (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY) { + if (pinctrl_state->iodelay) + late_recalibrate_iodelay(pinctrl_state->padconf, + pinctrl_state->npads, + pinctrl_state->iodelay, + pinctrl_state->niodelays); + else + do_set_mux32((*ctrl)->control_padconf_core_base, + pinctrl_state->padconf, + pinctrl_state->npads); + } +} +#endif static void omap_hsmmc_set_timing(struct mmc *mmc) { u32 val; @@ -269,6 +348,7 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) mmc_base = priv->base_addr; + omap_hsmmc_stop_clock(mmc_base); val = readl(&mmc_base->ac12); val &= ~AC12_UHSMC_MASK; priv->mode = mmc->selected_mode; @@ -306,6 +386,11 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) break; } writel(val, &mmc_base->ac12); + +#ifdef CONFIG_IODELAY_RECALIBRATION + omap_hsmmc_io_recalibrate(mmc); +#endif + omap_hsmmc_start_clock(mmc_base); } static void omap_hsmmc_conf_bus_power(struct mmc *mmc) @@ -1290,10 +1375,271 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, return 0; } #else + +#ifdef CONFIG_IODELAY_RECALIBRATION +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct pad_conf_entry *padconf; + + padconf = (struct pad_conf_entry *)malloc(sizeof(*padconf) * count); + if (!padconf) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + padconf[index].offset = fdt32_to_cpu(pinctrl[2 * index]); + padconf[index].val = fdt32_to_cpu(pinctrl[2 * index + 1]); + index++; + } + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay_cfg_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct iodelay_cfg_entry *iodelay; + + iodelay = (struct iodelay_cfg_entry *)malloc(sizeof(*iodelay) * count); + if (!iodelay) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + iodelay[index].offset = fdt32_to_cpu(pinctrl[3 * index]); + iodelay[index].a_delay = fdt32_to_cpu(pinctrl[3 * index + 1]); + iodelay[index].g_delay = fdt32_to_cpu(pinctrl[3 * index + 2]); + index++; + } + + return iodelay; +} + +static const fdt32_t *omap_hsmmc_get_pinctrl_entry(u32 phandle, + const char *name, int *len) +{ + const void *fdt = gd->fdt_blob; + int offset; + const fdt32_t *pinctrl; + + offset = fdt_node_offset_by_phandle(fdt, phandle); + if (offset < 0) { + debug("failed to get pinctrl node %s.\n", + fdt_strerror(offset)); + return 0; + } + + pinctrl = fdt_getprop(fdt, offset, name, len); + if (!pinctrl) { + debug("failed to get property %s\n", name); + return 0; + } + + return pinctrl; +} + +static uint32_t omap_hsmmc_get_pad_conf_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, NULL); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + return fdt32_to_cpu(*phandle); +} + +static uint32_t omap_hsmmc_get_iodelay_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int len; + int count; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, &len); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + /* No manual mode iodelay values if count < 2 */ + count = len / sizeof(*phandle); + if (count < 2) + return 0; + + return fdt32_to_cpu(*(phandle + 1)); +} + +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf(struct mmc *mmc, char *prop_name, int *npads) +{ + int len; + int count; + struct pad_conf_entry *padconf; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_pad_conf_phandle(mmc, prop_name); + if (!phandle) + return ERR_PTR(-EINVAL); + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-single,pins", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 2; + padconf = omap_hsmmc_get_pad_conf_entry(pinctrl, count); + if (!padconf) + return ERR_PTR(-EINVAL); + + *npads = count; + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay(struct mmc *mmc, char *prop_name, int *niodelay) +{ + int len; + int count; + struct iodelay_cfg_entry *iodelay; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_iodelay_phandle(mmc, prop_name); + /* Not all modes have manual mode iodelay values. So its not fatal */ + if (!phandle) + return 0; + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-pin-array", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 3; + iodelay = omap_hsmmc_get_iodelay_cfg_entry(pinctrl, count); + if (!iodelay) + return ERR_PTR(-EINVAL); + + *niodelay = count; + + return iodelay; +} + +static struct omap_hsmmc_pinctrl_state * +omap_hsmmc_get_pinctrl_by_mode(struct mmc *mmc, char *mode) +{ + int index; + int npads = 0; + int niodelays = 0; + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(mmc->dev); + char prop_name[11]; + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + pinctrl_state = (struct omap_hsmmc_pinctrl_state *) + malloc(sizeof(*pinctrl_state)); + if (!pinctrl_state) { + debug("failed to allocate memory\n"); + return 0; + } + + index = fdt_stringlist_search(fdt, node, "pinctrl-names", mode); + if (index < 0) { + debug("fail to find %s mode %s\n", mode, fdt_strerror(index)); + goto err_pinctrl_state; + } + + sprintf(prop_name, "pinctrl-%d", index); + + pinctrl_state->padconf = omap_hsmmc_get_pad_conf(mmc, prop_name, + &npads); + if (IS_ERR(pinctrl_state->padconf)) + goto err_pinctrl_state; + pinctrl_state->npads = npads; + + pinctrl_state->iodelay = omap_hsmmc_get_iodelay(mmc, prop_name, + &niodelays); + if (IS_ERR(pinctrl_state->iodelay)) + goto err_padconf; + pinctrl_state->niodelays = niodelays; + + return pinctrl_state; + +err_padconf: + kfree(pinctrl_state->padconf); + +err_pinctrl_state: + kfree(pinctrl_state); + return 0; +} + +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ + do { \ + struct omap_hsmmc_pinctrl_state *s; \ + if (!(cfg->host_caps & capmask)) \ + break; \ + \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ + if (!s) { \ + debug("%s: no pinctrl for %s\n", \ + mmc->dev->name, #mode); \ + cfg->host_caps &= ~(capmask); \ + } else { \ + priv->mode##_pinctrl_state = s; \ + } \ + } while (0) + +static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct mmc_config *cfg = omap_hsmmc_get_cfg(mmc); + struct omap_hsmmc_pinctrl_state *default_pinctrl; + + if (!(priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) + return 0; + + default_pinctrl = omap_hsmmc_get_pinctrl_by_mode(mmc, "default"); + if (!default_pinctrl) { + printf("no pinctrl state for default mode\n"); + return -EINVAL; + } + + priv->default_pinctrl_state = default_pinctrl; + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12); + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v); + OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs); + + return 0; +} +#endif + #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) { struct omap_hsmmc_plat *plat = dev_get_platdata(dev); + struct omap_mmc_of_data *of_data = (void *)dev_get_driver_data(dev); + struct mmc_config *cfg = &plat->cfg; const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); @@ -1315,6 +1661,8 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; if (fdtdec_get_bool(fdt, node, "no-1-8-v")) plat->controller_flags |= OMAP_HSMMC_NO_1_8_V; + if (of_data) + plat->controller_flags |= of_data->controller_flags; #ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted"); @@ -1340,9 +1688,13 @@ static int omap_hsmmc_probe(struct udevice *dev) struct omap_hsmmc_data *priv = dev_get_priv(dev); struct mmc_config *cfg = &plat->cfg; struct mmc *mmc; +#ifdef CONFIG_IODELAY_RECALIBRATION + int ret; +#endif cfg->name = "OMAP SD/MMC"; priv->base_addr = plat->base_addr; + priv->controller_flags = plat->controller_flags; #ifdef OMAP_HSMMC_USE_GPIO priv->cd_inverted = plat->cd_inverted; #endif @@ -1363,14 +1715,34 @@ static int omap_hsmmc_probe(struct udevice *dev) mmc->dev = dev; upriv->mmc = mmc; +#ifdef CONFIG_IODELAY_RECALIBRATION + ret = omap_hsmmc_get_pinctrl_state(mmc); + /* + * disable high speed modes for the platforms that require IO delay + * and for which we don't have this information + */ + if ((ret < 0) && + (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) { + priv->controller_flags &= ~OMAP_HSMMC_REQUIRE_IODELAY; + cfg->host_caps &= ~(MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_DDR_52) | + UHS_CAPS); + } +#endif + return omap_hsmmc_init_setup(mmc); } #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + +static const struct omap_mmc_of_data dra7_mmc_of_data = { + .controller_flags = OMAP_HSMMC_REQUIRE_IODELAY, +}; + static const struct udevice_id omap_hsmmc_ids[] = { { .compatible = "ti,omap3-hsmmc" }, { .compatible = "ti,omap4-hsmmc" }, { .compatible = "ti,am33xx-hsmmc" }, + { .compatible = "ti,dra7-hsmmc", .data = (ulong)&dra7_mmc_of_data }, { } }; #endif From 2d28eeda33a372f7c5f1219728d4c928723ebed1 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:41 +0100 Subject: [PATCH 13/28] mmc: omap_hsmmc: Add support to get pinctrl values and max frequency for different hw revisions AM572x SR1.1 requires different IODelay values to be used than that used in AM572x SR2.0. These values are populated in device tree. Add capability in omap_hsmmc driver to extract IOdelay values for different silicon revision. The maximum frequency is also reduced when using a ES1.1. To keep the ability to boot both revsions with the same dtb, those values can be provided by the platform code. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/arch-omap5/sys_proto.h | 7 +++ arch/arm/include/asm/omap_mmc.h | 1 + drivers/mmc/omap_hsmmc.c | 58 ++++++++++++++++----- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/arch/arm/include/asm/arch-omap5/sys_proto.h b/arch/arm/include/asm/arch-omap5/sys_proto.h index a6b3557b50..d43cd7f2e7 100644 --- a/arch/arm/include/asm/arch-omap5/sys_proto.h +++ b/arch/arm/include/asm/arch-omap5/sys_proto.h @@ -35,6 +35,12 @@ struct pad_conf_entry { u32 val; }; +struct mmc_platform_fixups { + const char *hw_rev; + u32 unsupported_caps; + u32 max_freq; +}; + struct omap_sysinfo { char *board_string; }; @@ -71,6 +77,7 @@ void force_emif_self_refresh(void); void get_ioregs(const struct ctrl_ioregs **regs); void srcomp_enable(void); void setup_warmreset_time(void); +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr); static inline u32 div_round_up(u32 num, u32 den) { diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 635ce1e488..c50087e135 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -70,6 +70,7 @@ struct omap_hsmmc_plat { struct mmc mmc; bool cd_inverted; u32 controller_flags; + const char *hw_rev; }; /* diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2b77422eee..766cd09f7a 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -96,6 +96,7 @@ struct omap_hsmmc_data { struct omap_hsmmc_adma_desc *adma_desc_table; uint desc_slot; #endif + const char *hw_rev; #ifdef CONFIG_IODELAY_RECALIBRATION struct omap_hsmmc_pinctrl_state *default_pinctrl_state; struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; @@ -1368,6 +1369,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, if ((get_cpu_family() == CPU_OMAP34XX) && (get_cpu_rev() <= CPU_3XX_ES21)) cfg->b_max = 1; #endif + mmc = mmc_create(cfg, priv); if (mmc == NULL) return -1; @@ -1587,20 +1589,28 @@ err_pinctrl_state: return 0; } -#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ - do { \ - struct omap_hsmmc_pinctrl_state *s; \ - if (!(cfg->host_caps & capmask)) \ - break; \ - \ - s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ - if (!s) { \ - debug("%s: no pinctrl for %s\n", \ - mmc->dev->name, #mode); \ - cfg->host_caps &= ~(capmask); \ - } else { \ - priv->mode##_pinctrl_state = s; \ - } \ +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ + do { \ + struct omap_hsmmc_pinctrl_state *s = NULL; \ + char str[20]; \ + if (!(cfg->host_caps & capmask)) \ + break; \ + \ + if (priv->hw_rev) { \ + sprintf(str, "%s-%s", #mode, priv->hw_rev); \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, str); \ + } \ + \ + if (!s) \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ + \ + if (!s) { \ + debug("%s: no pinctrl for %s\n", \ + mmc->dev->name, #mode); \ + cfg->host_caps &= ~(capmask); \ + } else { \ + priv->mode##_pinctrl_state = s; \ + } \ } while (0) static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) @@ -1635,12 +1645,22 @@ static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) #endif #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +#ifdef CONFIG_OMAP54XX +__weak const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + return NULL; +} +#endif + static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) { struct omap_hsmmc_plat *plat = dev_get_platdata(dev); struct omap_mmc_of_data *of_data = (void *)dev_get_driver_data(dev); struct mmc_config *cfg = &plat->cfg; +#ifdef CONFIG_OMAP54XX + const struct mmc_platform_fixups *fixups; +#endif const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); int ret; @@ -1664,6 +1684,15 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) if (of_data) plat->controller_flags |= of_data->controller_flags; +#ifdef CONFIG_OMAP54XX + fixups = platform_fixups_mmc(devfdt_get_addr(dev)); + if (fixups) { + plat->hw_rev = fixups->hw_rev; + cfg->host_caps &= ~fixups->unsupported_caps; + cfg->f_max = fixups->max_freq; + } +#endif + #ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted"); #endif @@ -1695,6 +1724,7 @@ static int omap_hsmmc_probe(struct udevice *dev) cfg->name = "OMAP SD/MMC"; priv->base_addr = plat->base_addr; priv->controller_flags = plat->controller_flags; + priv->hw_rev = plat->hw_rev; #ifdef OMAP_HSMMC_USE_GPIO priv->cd_inverted = plat->cd_inverted; #endif From bcc6bd84d48cfc5f7b777feb2e2a6e264b270252 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:42 +0100 Subject: [PATCH 14/28] mmc: omap_hsmmc: allow the simple HS modes to use the default pinctrl The default configuration is usually working fine for the the HS modes. Don't enforce the presence of a dedicated pinmux for the HS modes. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 766cd09f7a..37fa7a49c4 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -328,6 +328,9 @@ static void omap_hsmmc_io_recalibrate(struct mmc *mmc) break; } + if (!pinctrl_state) + pinctrl_state = priv->default_pinctrl_state; + if (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY) { if (pinctrl_state->iodelay) late_recalibrate_iodelay(pinctrl_state->padconf, @@ -1589,7 +1592,7 @@ err_pinctrl_state: return 0; } -#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode, optional) \ do { \ struct omap_hsmmc_pinctrl_state *s = NULL; \ char str[20]; \ @@ -1604,7 +1607,7 @@ err_pinctrl_state: if (!s) \ s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ \ - if (!s) { \ + if (!s && !optional) { \ debug("%s: no pinctrl for %s\n", \ mmc->dev->name, #mode); \ cfg->host_caps &= ~(capmask); \ @@ -1630,15 +1633,15 @@ static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) priv->default_pinctrl_state = default_pinctrl; - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12, false); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v); - OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v); - OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs, true); return 0; } From 3149c13ac324dae0b8993a4f6c964a261aa691a9 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:43 +0100 Subject: [PATCH 15/28] mmc: omap_hsmmc: update mmc->clock with the actual bus speed When the clock is applied, compute the actual value of the clock. It may be slightly different from the requested value (max freq, divisor threshold) Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 37fa7a49c4..b10d55ef61 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -1147,7 +1147,8 @@ static void omap_hsmmc_set_clock(struct mmc *mmc) } } - priv->clock = mmc->clock; + priv->clock = MMC_CLOCK_REFERENCE * 1000000 / dsor; + mmc->clock = priv->clock; omap_hsmmc_start_clock(mmc_base); } From 42182c9b9ca946b68f564936ffc181b75ce3da9f Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:44 +0100 Subject: [PATCH 16/28] mmc: omap_hsmmc: implement send_init_stream callback This callback is used to send the 74 clock cycles after power up. Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index b10d55ef61..71608d18e2 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -46,6 +46,7 @@ #include #endif #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -564,6 +565,14 @@ tuning_error: return ret; } #endif + +static void omap_hsmmc_send_init_stream(struct udevice *dev) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct hsmmc *mmc_base = priv->base_addr; + + mmc_init_stream(mmc_base); +} #endif static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd) @@ -652,7 +661,10 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl); mmc_enable_irq(mmc, NULL); + +#if !CONFIG_IS_ENABLED(DM_MMC) mmc_init_stream(mmc_base); +#endif return 0; } @@ -1279,6 +1291,7 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { #ifdef MMC_SUPPORTS_TUNING .execute_tuning = omap_hsmmc_execute_tuning, #endif + .send_init_stream = omap_hsmmc_send_init_stream, }; #else static const struct mmc_ops omap_hsmmc_ops = { From 90321dce0da1ba0844a7066a1034cdf73b8613c6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:45 +0100 Subject: [PATCH 17/28] mmc: omap_hsmmc: allow mmc clock to be gated mmc core has defined a new parameter *clk_disable* to gate the clock. Disable the clock here if *clk_disable* is set. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- drivers/mmc/omap_hsmmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 71608d18e2..0e80420d8b 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -1207,6 +1207,7 @@ static int omap_hsmmc_set_ios(struct udevice *dev) struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = upriv->mmc; #endif + struct hsmmc *mmc_base = priv->base_addr; if (priv->bus_width != mmc->bus_width) omap_hsmmc_set_bus_width(mmc); @@ -1214,6 +1215,11 @@ static int omap_hsmmc_set_ios(struct udevice *dev) if (priv->clock != mmc->clock) omap_hsmmc_set_clock(mmc); + if (mmc->clk_disable) + omap_hsmmc_stop_clock(mmc_base); + else + omap_hsmmc_start_clock(mmc_base); + #if CONFIG_IS_ENABLED(DM_MMC) if (priv->mode != mmc->selected_mode) omap_hsmmc_set_timing(mmc); From 04f9f8be8301ec7a9be6371c558fd20a7b9a179a Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:46 +0100 Subject: [PATCH 18/28] mmc: omap_hsmmc: add signal voltage selection support I/O data lines of UHS SD card operates at 1.8V when in UHS speed mode (same is true for eMMC in DDR and HS200 modes). Add support to switch signal voltage to 1.8V in order to support UHS cards and eMMC HS200 and DDR modes. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 10 +- drivers/mmc/omap_hsmmc.c | 176 ++++++++++++++++++++++++++++---- 2 files changed, 160 insertions(+), 26 deletions(-) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index c50087e135..c6129c5348 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -172,10 +172,6 @@ struct omap_hsmmc_plat { #define VS30_3V0SUP BIT(25) #define VS18_1V8SUP BIT(26) -#define IOV_3V3 3300000 -#define IOV_3V0 3000000 -#define IOV_1V8 1800000 - #define AC12_ET BIT(22) #define AC12_V1V8_SIGEN BIT(19) #define AC12_SCLK_SEL BIT(23) @@ -224,6 +220,12 @@ struct omap_hsmmc_plat { IE_DTO | IE_CIE | IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO |\ IE_BRR | IE_BWR | IE_TC | IE_CC) +#define CON_CLKEXTFREE BIT(16) +#define CON_PADEN BIT(15) +#define PSTATE_CLEV BIT(24) +#define PSTATE_DLEV (0xF << 20) +#define PSTATE_DLEV_DAT0 (0x1 << 20) + int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, int wp_gpio); diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 0e80420d8b..24027f2b34 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -78,6 +78,7 @@ struct omap_hsmmc_data { #endif uint bus_width; uint clock; + ushort last_cmd; #ifdef OMAP_HSMMC_USE_GPIO #if CONFIG_IS_ENABLED(DM_MMC) struct gpio_desc cd_gpio; /* Change Detect GPIO */ @@ -89,7 +90,6 @@ struct omap_hsmmc_data { #endif #endif #if CONFIG_IS_ENABLED(DM_MMC) - uint iov; enum bus_mode mode; #endif u8 controller_flags; @@ -98,6 +98,8 @@ struct omap_hsmmc_data { uint desc_slot; #endif const char *hw_rev; + struct udevice *pbias_supply; + uint signal_voltage; #ifdef CONFIG_IODELAY_RECALIBRATION struct omap_hsmmc_pinctrl_state *default_pinctrl_state; struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; @@ -254,7 +256,8 @@ static unsigned char mmc_board_init(struct mmc *mmc) &prcm_base->iclken1_core); #endif -#if defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX) +#if (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) &&\ + !CONFIG_IS_ENABLED(DM_REGULATOR) /* PBIAS config needed for MMC1 only */ if (mmc_get_blk_desc(mmc)->devnum == 0) vmmc_pbias_config(LDO_VOLT_3V0); @@ -398,32 +401,148 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) omap_hsmmc_start_clock(mmc_base); } -static void omap_hsmmc_conf_bus_power(struct mmc *mmc) +static void omap_hsmmc_conf_bus_power(struct mmc *mmc, uint signal_voltage) { struct hsmmc *mmc_base; struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); - u32 val; + u32 hctl, ac12; mmc_base = priv->base_addr; - val = readl(&mmc_base->hctl) & ~SDVS_MASK; + hctl = readl(&mmc_base->hctl) & ~SDVS_MASK; + ac12 = readl(&mmc_base->ac12) & ~AC12_V1V8_SIGEN; - switch (priv->iov) { - case IOV_3V3: - val |= SDVS_3V3; + switch (signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + hctl |= SDVS_3V0; break; - case IOV_3V0: - val |= SDVS_3V0; - break; - case IOV_1V8: - val |= SDVS_1V8; + case MMC_SIGNAL_VOLTAGE_180: + hctl |= SDVS_1V8; + ac12 |= AC12_V1V8_SIGEN; break; } - writel(val, &mmc_base->hctl); + writel(hctl, &mmc_base->hctl); + writel(ac12, &mmc_base->ac12); } -static void omap_hsmmc_set_capabilities(struct mmc *mmc) +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +static int omap_hsmmc_wait_dat0(struct udevice *dev, int state, int timeout) +{ + int ret = -ETIMEDOUT; + u32 con; + bool dat0_high; + bool target_dat0_high = !!state; + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct hsmmc *mmc_base = priv->base_addr; + + con = readl(&mmc_base->con); + writel(con | CON_CLKEXTFREE | CON_PADEN, &mmc_base->con); + + timeout = DIV_ROUND_UP(timeout, 10); /* check every 10 us. */ + while (timeout--) { + dat0_high = !!(readl(&mmc_base->pstate) & PSTATE_DLEV_DAT0); + if (dat0_high == target_dat0_high) { + ret = 0; + break; + } + udelay(10); + } + writel(con, &mmc_base->con); + + return ret; +} +#endif + +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) +#if CONFIG_IS_ENABLED(DM_REGULATOR) +static int omap_hsmmc_set_io_regulator(struct mmc *mmc, int mV) +{ + int ret = 0; + int uV = mV * 1000; + + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + + if (!mmc->vqmmc_supply) + return 0; + + /* Disable PBIAS */ + ret = regulator_set_enable(priv->pbias_supply, false); + if (ret && ret != -ENOSYS) + return ret; + + /* Turn off IO voltage */ + ret = regulator_set_enable(mmc->vqmmc_supply, false); + if (ret && ret != -ENOSYS) + return ret; + /* Program a new IO voltage value */ + ret = regulator_set_value(mmc->vqmmc_supply, uV); + if (ret) + return ret; + /* Turn on IO voltage */ + ret = regulator_set_enable(mmc->vqmmc_supply, true); + if (ret && ret != -ENOSYS) + return ret; + + /* Program PBIAS voltage*/ + ret = regulator_set_value(priv->pbias_supply, uV); + if (ret && ret != -ENOSYS) + return ret; + /* Enable PBIAS */ + ret = regulator_set_enable(priv->pbias_supply, true); + if (ret && ret != -ENOSYS) + return ret; + + return 0; +} +#endif + +static int omap_hsmmc_set_signal_voltage(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base = priv->base_addr; + int mv = mmc_voltage_to_mv(mmc->signal_voltage); + u32 capa_mask; + __maybe_unused u8 palmas_ldo_volt; + u32 val; + + if (mv < 0) + return -EINVAL; + + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + /* Use 3.0V rather than 3.3V */ + mv = 3000; + capa_mask = VS30_3V0SUP; + palmas_ldo_volt = LDO_VOLT_3V0; + } else if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + capa_mask = VS18_1V8SUP; + palmas_ldo_volt = LDO_VOLT_1V8; + } else { + return -EOPNOTSUPP; + } + + val = readl(&mmc_base->capa); + if (!(val & capa_mask)) + return -EOPNOTSUPP; + + priv->signal_voltage = mmc->signal_voltage; + + omap_hsmmc_conf_bus_power(mmc, mmc->signal_voltage); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + return omap_hsmmc_set_io_regulator(mmc, mv); +#elif (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) && \ + defined(CONFIG_PALMAS_POWER) + if (mmc_get_blk_desc(mmc)->devnum == 0) + vmmc_pbias_config(palmas_ldo_volt); + return 0; +#else + return 0; +#endif +} +#endif + +static uint32_t omap_hsmmc_set_capabilities(struct mmc *mmc) { struct hsmmc *mmc_base; struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -434,18 +553,17 @@ static void omap_hsmmc_set_capabilities(struct mmc *mmc) if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { val |= (VS30_3V0SUP | VS18_1V8SUP); - priv->iov = IOV_3V0; } else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) { val |= VS30_3V0SUP; val &= ~VS18_1V8SUP; - priv->iov = IOV_3V0; } else { val |= VS18_1V8SUP; val &= ~VS30_3V0SUP; - priv->iov = IOV_1V8; } writel(val, &mmc_base->capa); + + return val; } #ifdef MMC_SUPPORTS_TUNING @@ -630,8 +748,9 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) #endif #if CONFIG_IS_ENABLED(DM_MMC) - omap_hsmmc_set_capabilities(mmc); - omap_hsmmc_conf_bus_power(mmc); + reg_val = omap_hsmmc_set_capabilities(mmc); + omap_hsmmc_conf_bus_power(mmc, (reg_val & VS30_3V0SUP) ? + MMC_SIGNAL_VOLTAGE_330 : MMC_SIGNAL_VOLTAGE_180); #else writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, @@ -842,6 +961,7 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct hsmmc *mmc_base; unsigned int flags, mmc_stat; ulong start; + priv->last_cmd = cmd->cmdidx; mmc_base = priv->base_addr; @@ -1208,6 +1328,7 @@ static int omap_hsmmc_set_ios(struct udevice *dev) struct mmc *mmc = upriv->mmc; #endif struct hsmmc *mmc_base = priv->base_addr; + int ret = 0; if (priv->bus_width != mmc->bus_width) omap_hsmmc_set_bus_width(mmc); @@ -1223,8 +1344,13 @@ static int omap_hsmmc_set_ios(struct udevice *dev) #if CONFIG_IS_ENABLED(DM_MMC) if (priv->mode != mmc->selected_mode) omap_hsmmc_set_timing(mmc); + +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) + if (priv->signal_voltage != mmc->signal_voltage) + ret = omap_hsmmc_set_signal_voltage(mmc); #endif - return 0; +#endif + return ret; } #ifdef OMAP_HSMMC_USE_GPIO @@ -1298,6 +1424,9 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { .execute_tuning = omap_hsmmc_execute_tuning, #endif .send_init_stream = omap_hsmmc_send_init_stream, +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + .wait_dat0 = omap_hsmmc_wait_dat0, +#endif }; #else static const struct mmc_ops omap_hsmmc_ops = { @@ -1759,7 +1888,10 @@ static int omap_hsmmc_probe(struct udevice *dev) if (mmc == NULL) return -1; #endif - +#if CONFIG_IS_ENABLED(DM_REGULATOR) + device_get_supply_regulator(dev, "pbias-supply", + &priv->pbias_supply); +#endif #if defined(OMAP_HSMMC_USE_GPIO) && CONFIG_IS_ENABLED(OF_CONTROL) gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN); gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN); From 2022270c7ddaaad1e1c4e78e5d03e4df0a8ce222 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:47 +0100 Subject: [PATCH 19/28] ARM: OMAP5: set mmc clock frequency to 192MHz Now that omap_hsmmc has support for hs200 mode, change the clock frequency to 192MHz. Also change the REFERENCE CLOCK frequency to 192MHz based on which the internal mmc clock divider is calculated. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/arch-omap5/clock.h | 2 +- arch/arm/include/asm/omap_mmc.h | 4 ++++ arch/arm/mach-omap2/omap5/hw_data.c | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/arch-omap5/clock.h b/arch/arm/include/asm/arch-omap5/clock.h index ee2e78b99b..3d718c02df 100644 --- a/arch/arm/include/asm/arch-omap5/clock.h +++ b/arch/arm/include/asm/arch-omap5/clock.h @@ -135,7 +135,7 @@ /* CM_L3INIT_HSMMCn_CLKCTRL */ #define HSMMC_CLKCTRL_CLKSEL_MASK (1 << 24) -#define HSMMC_CLKCTRL_CLKSEL_DIV_MASK (1 << 25) +#define HSMMC_CLKCTRL_CLKSEL_DIV_MASK (3 << 25) /* CM_L3INIT_SATA_CLKCTRL */ #define SATA_CLKCTRL_OPTFCLKEN_MASK (1 << 8) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index c6129c5348..3d70148882 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -199,7 +199,11 @@ struct omap_hsmmc_plat { #define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE) /* Clock Configurations and Macros */ +#ifdef CONFIG_OMAP54XX +#define MMC_CLOCK_REFERENCE 192 /* MHz */ +#else #define MMC_CLOCK_REFERENCE 96 /* MHz */ +#endif /* DLL */ #define DLL_SWT BIT(20) diff --git a/arch/arm/mach-omap2/omap5/hw_data.c b/arch/arm/mach-omap2/omap5/hw_data.c index bb05e1920b..7fc38368c9 100644 --- a/arch/arm/mach-omap2/omap5/hw_data.c +++ b/arch/arm/mach-omap2/omap5/hw_data.c @@ -438,17 +438,17 @@ void enable_basic_clocks(void) setbits_le32((*prcm)->cm_l4per_gpio4_clkctrl, GPIO4_CLKCTRL_OPTFCLKEN_MASK); - /* Enable 96 MHz clock for MMC1 & MMC2 */ + /* Enable 192 MHz clock for MMC1 & MMC2 */ setbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, HSMMC_CLKCTRL_CLKSEL_MASK); setbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, HSMMC_CLKCTRL_CLKSEL_MASK); /* Set the correct clock dividers for mmc */ - setbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, - HSMMC_CLKCTRL_CLKSEL_DIV_MASK); - setbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, - HSMMC_CLKCTRL_CLKSEL_DIV_MASK); + clrbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, + HSMMC_CLKCTRL_CLKSEL_DIV_MASK); + clrbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, + HSMMC_CLKCTRL_CLKSEL_DIV_MASK); /* Select 32KHz clock as the source of GPTIMER1 */ setbits_le32((*prcm)->cm_wkup_gptimer1_clkctrl, From 6ba41e5dc320c3983d90808e172bd15fdd4abdf4 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:48 +0100 Subject: [PATCH 20/28] ARM: dts: DRA7: use new dra7-specific compatible string Use the new compatible string "ti,dra7-hsmmc" that was specifically added for dra7 and dra72. This is required since for dra7 and dra72 processors iodelay values has to be set unlike other processors. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Sekhar Nori Signed-off-by: Jean-Jacques Hiblot --- arch/arm/dts/dra7.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/dts/dra7.dtsi b/arch/arm/dts/dra7.dtsi index 02a136a466..906184318b 100644 --- a/arch/arm/dts/dra7.dtsi +++ b/arch/arm/dts/dra7.dtsi @@ -1056,7 +1056,7 @@ }; mmc1: mmc@4809c000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x4809c000 0x400>; interrupts = ; ti,hwmods = "mmc1"; @@ -1070,7 +1070,7 @@ }; mmc2: mmc@480b4000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480b4000 0x400>; interrupts = ; ti,hwmods = "mmc2"; @@ -1082,7 +1082,7 @@ }; mmc3: mmc@480ad000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480ad000 0x400>; interrupts = ; ti,hwmods = "mmc3"; @@ -1095,7 +1095,7 @@ }; mmc4: mmc@480d1000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480d1000 0x400>; interrupts = ; ti,hwmods = "mmc4"; From 2adee41db99e5a6ef7477a2d676ef3d591dd3d76 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:49 +0100 Subject: [PATCH 21/28] ARM: dts: dra7: Add supported MMC/SD modes in MMC dt nodes On DRA7 family SoCs, MMC1 controller supports SDR104, SDR50, DDR50, SDR25 and SDR12 UHS modes. MMC2 controller supports HS200 and DDR modes. MMC3 controller supports SDR12, SDR25 and SDR50 modes. MMC4 controller supports SDR12 and SDR25 modes. Add these supported modes in device-tree file. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/dts/dra7.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm/dts/dra7.dtsi b/arch/arm/dts/dra7.dtsi index 906184318b..0f982d8b44 100644 --- a/arch/arm/dts/dra7.dtsi +++ b/arch/arm/dts/dra7.dtsi @@ -1067,6 +1067,11 @@ status = "disabled"; pbias-supply = <&pbias_mmc_reg>; max-frequency = <192000000>; + sd-uhs-sdr104; + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr25; + sd-uhs-sdr12; }; mmc2: mmc@480b4000 { @@ -1079,6 +1084,10 @@ dma-names = "tx", "rx"; status = "disabled"; max-frequency = <192000000>; + sd-uhs-sdr25; + sd-uhs-sdr12; + mmc-hs200-1_8v; + mmc-ddr-1_8v; }; mmc3: mmc@480ad000 { @@ -1092,6 +1101,9 @@ status = "disabled"; /* Errata i887 limits max-frequency of MMC3 to 64 MHz */ max-frequency = <64000000>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; }; mmc4: mmc@480d1000 { @@ -1104,6 +1116,8 @@ dma-names = "tx", "rx"; status = "disabled"; max-frequency = <192000000>; + sd-uhs-sdr12; + sd-uhs-sdr25; }; mmu0_dsp1: mmu@40d01000 { From 4db25fe7fa0e68196cf3a89dae7f9632aecb3abd Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:50 +0100 Subject: [PATCH 22/28] dts: am57xx-beagle-x15: disable UHS and HS200 support The UHS modes are not supported in beagle-x15 because it's not possible to switch the IO lines supply voltage to 1.8v. Also HS200 cannot be supported on mmc2, because the IO lines of mmc2 are connected to 3.3v. Signed-off-by: Jean-Jacques Hiblot --- arch/arm/dts/am57xx-beagle-x15.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/dts/am57xx-beagle-x15.dts b/arch/arm/dts/am57xx-beagle-x15.dts index d6689106d2..8d9bdf10a7 100644 --- a/arch/arm/dts/am57xx-beagle-x15.dts +++ b/arch/arm/dts/am57xx-beagle-x15.dts @@ -25,6 +25,11 @@ pinctrl-1 = <&mmc1_pins_hs>; vmmc-supply = <&ldo1_reg>; + /delete-property/ sd-uhs-sdr104; + /delete-property/ sd-uhs-sdr50; + /delete-property/ sd-uhs-ddr50; + /delete-property/ sd-uhs-sdr25; + /delete-property/ sd-uhs-sdr12; }; &mmc2 { @@ -32,6 +37,7 @@ pinctrl-0 = <&mmc2_pins_default>; pinctrl-1 = <&mmc2_pins_hs>; pinctrl-2 = <&mmc2_pins_ddr_3_3v_rev11 &mmc2_iodelay_ddr_3_3v_rev11_conf>; + /delete-property/ mmc-hs200-1_8v; }; /* errata i880 "Ethernet RGMII2 Limited to 10/100 Mbps" */ From e74adafb04b908a5f007994138e46237a49e9bfd Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:51 +0100 Subject: [PATCH 23/28] dts: am57xx-idk: disable HS200 support HS200 cannot be supported on mmc2, because the IO lines of mmc2 are connected to 3.3v. Signed-off-by: Jean-Jacques Hiblot --- arch/arm/dts/am57xx-idk-common.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/dts/am57xx-idk-common.dtsi b/arch/arm/dts/am57xx-idk-common.dtsi index 97aa8e6a56..fa5a078b27 100644 --- a/arch/arm/dts/am57xx-idk-common.dtsi +++ b/arch/arm/dts/am57xx-idk-common.dtsi @@ -413,6 +413,8 @@ bus-width = <8>; ti,non-removable; max-frequency = <96000000>; + no-1-8-v; + /delete-property/ mmc-hs200-1_8v; }; &dcan1 { From c413baa95c6ce4adda3646f0c4d5a3119f463de6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:52 +0100 Subject: [PATCH 24/28] ARM: DRA7x/AM57x: Add MMC/SD fixups for rev1.0 and rev 1.1 Since DRA7xx/AM57xx SR1.1 and SR1.0 has errata to limit the frequency of MMC1 to 96MHz and frequency of MMC2 to 48MHz for AM572x SR1.1, limit the frequency and disable higher speed modes for those revision. Also use the recommended IO delays (those tagged with "rev11") Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- board/ti/am57xx/board.c | 30 ++++++++++++++++++++++++++++++ board/ti/dra7xx/evm.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/board/ti/am57xx/board.c b/board/ti/am57xx/board.c index c3f60f60c8..5bd8778563 100644 --- a/board/ti/am57xx/board.c +++ b/board/ti/am57xx/board.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "../common/board_detect.h" #include "mux_data.h" @@ -815,6 +816,35 @@ int board_mmc_init(bd_t *bis) omap_mmc_init(1, 0, 0, -1, -1); return 0; } + +static const struct mmc_platform_fixups am57x_es1_1_mmc1_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104), + .max_freq = 96000000, +}; + +static const struct mmc_platform_fixups am57x_es1_1_mmc23_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104) | + MMC_CAP(UHS_SDR50), + .max_freq = 48000000, +}; + +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + switch (omap_revision()) { + case DRA752_ES1_0: + case DRA752_ES1_1: + if (addr == OMAP_HSMMC1_BASE) + return &am57x_es1_1_mmc1_fixups; + else + return &am57x_es1_1_mmc23_fixups; + default: + return NULL; + } +} #endif #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_OS_BOOT) diff --git a/board/ti/dra7xx/evm.c b/board/ti/dra7xx/evm.c index 519475e01f..06f061c92c 100644 --- a/board/ti/dra7xx/evm.c +++ b/board/ti/dra7xx/evm.c @@ -866,6 +866,35 @@ void board_mmc_poweron_ldo(uint voltage) palmas_mmc1_poweron_ldo(LDO1_VOLTAGE, LDO1_CTRL, voltage); } } + +static const struct mmc_platform_fixups dra7x_es1_1_mmc1_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104), + .max_freq = 96000000, +}; + +static const struct mmc_platform_fixups dra7x_es1_1_mmc23_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104) | + MMC_CAP(UHS_SDR50), + .max_freq = 48000000, +}; + +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + switch (omap_revision()) { + case DRA752_ES1_0: + case DRA752_ES1_1: + if (addr == OMAP_HSMMC1_BASE) + return &dra7x_es1_1_mmc1_fixups; + else + return &dra7x_es1_1_mmc23_fixups; + default: + return NULL; + } +} #endif #ifdef CONFIG_USB_DWC3 From 127a6011eead5fb7fbb9c62826034892591bc712 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:53 +0100 Subject: [PATCH 25/28] configs: dra7xx_evm/dra7xx_hs_evm: Enable MMC HS200 and SD UHS support By default UHS and HS200 are not enabled. Signed-off-by: Jean-Jacques Hiblot --- configs/dra7xx_evm_defconfig | 3 +++ configs/dra7xx_hs_evm_defconfig | 3 +++ 2 files changed, 6 insertions(+) diff --git a/configs/dra7xx_evm_defconfig b/configs/dra7xx_evm_defconfig index b13a27e365..e79e6d6737 100644 --- a/configs/dra7xx_evm_defconfig +++ b/configs/dra7xx_evm_defconfig @@ -49,6 +49,9 @@ CONFIG_DM_GPIO=y CONFIG_PCF8575_GPIO=y CONFIG_DM_I2C=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_MMC_OMAP_HS=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH=y diff --git a/configs/dra7xx_hs_evm_defconfig b/configs/dra7xx_hs_evm_defconfig index 7ccb4f0bce..bc15fd8f3a 100644 --- a/configs/dra7xx_hs_evm_defconfig +++ b/configs/dra7xx_hs_evm_defconfig @@ -51,6 +51,9 @@ CONFIG_DM_GPIO=y CONFIG_PCF8575_GPIO=y CONFIG_DM_I2C=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_MMC_OMAP_HS=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH=y From a0276f3eeed539cbc5ecba694030519dcd1fe308 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 9 Feb 2018 12:09:27 +0100 Subject: [PATCH 26/28] mmc: Fix bug in sd_set_card_speed() After settings the speed of the sd with the switch command, a check is done to make sure that the new speed has been set. The current check has a masking error: speed are encoded on 4 bits only. Fix it by masking the upper bits. This fixes a problem seen with QEmu emulating a vexpress-a15. Reported-by: Jonathan Gray Signed-off-by: Jean-Jacques Hiblot Tested-by: Jonathan Gray --- drivers/mmc/mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 3a2e3b353f..b46e3f6024 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1333,7 +1333,7 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) if (err) return err; - if ((__be32_to_cpu(switch_status[4]) >> 24) != speed) + if (((__be32_to_cpu(switch_status[4]) >> 24) & 0xF) != speed) return -ENOTSUPP; return 0; From ace1bed327411cf3cade45599864df2d461045a0 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 9 Feb 2018 12:09:28 +0100 Subject: [PATCH 27/28] mmc: fix bug in mmc_startup_v4() The correspondence between mmc versions as used in u-boot and the version numbers reported in register EXT_CSD_REV is wrong for versions above and including MMC_VERSION_4_41. All those versions were shifted by one: real 4.5 hardware appeared to be MMC_VERSION_5_0. Fix this by adding the missing version in the correspondence table. Reported-by: eil Eilmsteiner Heribert Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass Reviewed-by: Sam Protsenko --- drivers/mmc/mmc.c | 1 + include/mmc.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b46e3f6024..fb303dc21e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1950,6 +1950,7 @@ static int mmc_startup_v4(struct mmc *mmc) MMC_VERSION_4_1, MMC_VERSION_4_2, MMC_VERSION_4_3, + MMC_VERSION_4_4, MMC_VERSION_4_41, MMC_VERSION_4_5, MMC_VERSION_5_0, diff --git a/include/mmc.h b/include/mmc.h index a46eaed746..86f885b504 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -54,6 +54,7 @@ #define MMC_VERSION_4_1 MAKE_MMC_VERSION(4, 1, 0) #define MMC_VERSION_4_2 MAKE_MMC_VERSION(4, 2, 0) #define MMC_VERSION_4_3 MAKE_MMC_VERSION(4, 3, 0) +#define MMC_VERSION_4_4 MAKE_MMC_VERSION(4, 4, 0) #define MMC_VERSION_4_41 MAKE_MMC_VERSION(4, 4, 1) #define MMC_VERSION_4_5 MAKE_MMC_VERSION(4, 5, 0) #define MMC_VERSION_5_0 MAKE_MMC_VERSION(5, 0, 0) From 4a41fec589b5f57bc83fca423222c6a1860a50f5 Mon Sep 17 00:00:00 2001 From: Alex Kiernan Date: Fri, 9 Feb 2018 15:24:38 +0000 Subject: [PATCH 28/28] mmc: Fix uninitialised priv member When using omap_hsmmc without the device model then the allocation of mmc->priv ends up uninitialised. Signed-off-by: Alex Kiernan Tested-by: Robert Nelson Reviewed-by: Sam Protsenko --- drivers/mmc/omap_hsmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 24027f2b34..02970f29b2 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -1449,7 +1449,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, struct mmc_config *cfg; uint host_caps_val; - priv = malloc(sizeof(*priv)); + priv = calloc(1, sizeof(*priv)); if (priv == NULL) return -1;