From d4d64889b05561d3a3cc8741f86f76d577cb6149 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 28 Jan 2018 19:11:42 +0900 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 255310a8e61..3a2e3b353fe 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; } -- cgit v1.2.3 From 5baf543e5277ec30c2e9fdb3e89dafc926532bac Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:30 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 76 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index b12d6d9102e..fa8681ab699 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; } -- cgit v1.2.3 From 48a2f11443a1f15c1dcee4f2051f12edc6378510 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:31 +0100 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index fa8681ab699..713faab1109 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); -- cgit v1.2.3 From b59448170934094570a497ff7c5b4b2e94cff5a2 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:32 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 713faab1109..5141bf66e12 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"); -- cgit v1.2.3 From 8fc238bfada16d61dee4240de2d49f38a6a6f941 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:33 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 5141bf66e12..c6b74a1263f 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; } -- cgit v1.2.3 From 9b3fc21837dc32eda9656f264f74719ea77311a2 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:34 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index c6b74a1263f..2f4909e34bd 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: -- cgit v1.2.3 From 14761caeee3ba453d051c0db3246fbccc5a5b136 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:35 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2f4909e34bd..69a7c2eed4b 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 = { -- cgit v1.2.3 From 2faa1a302ba13ed65771d642eed126e458e41bf3 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:36 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 69a7c2eed4b..55232103e08 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); -- cgit v1.2.3 From a4efd73773c792737dd8d1e9d18da7796418fc1f Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:37 +0100 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 55232103e08..ab4a0952333 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; } } -- cgit v1.2.3 From 2d7482cf793fe4d8a98906002708d6e1fa2c5ba3 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:38 +0100 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index ab4a0952333..57548ee31f4 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")) -- cgit v1.2.3 From 33c1d77f4ad7dc7cc2e449c15a757a679f9237a6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:40 +0100 Subject: 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(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 57548ee31f4..2b77422eeeb 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 -- cgit v1.2.3 From 2d28eeda33a372f7c5f1219728d4c928723ebed1 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:41 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 58 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2b77422eeeb..766cd09f7a7 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 -- cgit v1.2.3 From bcc6bd84d48cfc5f7b777feb2e2a6e264b270252 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:42 +0100 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 766cd09f7a7..37fa7a49c43 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; } -- cgit v1.2.3 From 3149c13ac324dae0b8993a4f6c964a261aa691a9 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:43 +0100 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 37fa7a49c43..b10d55ef615 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); } -- cgit v1.2.3 From 42182c9b9ca946b68f564936ffc181b75ce3da9f Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:44 +0100 Subject: 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(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index b10d55ef615..71608d18e20 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 = { -- cgit v1.2.3 From 90321dce0da1ba0844a7066a1034cdf73b8613c6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 30 Jan 2018 16:01:45 +0100 Subject: 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(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 71608d18e20..0e80420d8bd 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); -- cgit v1.2.3 From 04f9f8be8301ec7a9be6371c558fd20a7b9a179a Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:46 +0100 Subject: 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 --- drivers/mmc/omap_hsmmc.c | 176 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 154 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 0e80420d8bd..24027f2b347 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; - break; - case IOV_3V0: - val |= SDVS_3V0; + switch (signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + hctl |= 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); -- cgit v1.2.3 From a0276f3eeed539cbc5ecba694030519dcd1fe308 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 9 Feb 2018 12:09:27 +0100 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 3a2e3b353fe..b46e3f60242 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; -- cgit v1.2.3 From ace1bed327411cf3cade45599864df2d461045a0 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 9 Feb 2018 12:09:28 +0100 Subject: 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 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b46e3f60242..fb303dc21e2 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, -- cgit v1.2.3 From 4a41fec589b5f57bc83fca423222c6a1860a50f5 Mon Sep 17 00:00:00 2001 From: Alex Kiernan Date: Fri, 9 Feb 2018 15:24:38 +0000 Subject: 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(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 24027f2b347..02970f29b29 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; -- cgit v1.2.3