diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/mmc.c | 28 | ||||
-rw-r--r-- | drivers/mmc/omap_hsmmc.c | 108 | ||||
-rw-r--r-- | drivers/mmc/renesas-sdhi.c | 10 | ||||
-rw-r--r-- | drivers/mmc/s5p_sdhci.c | 50 | ||||
-rw-r--r-- | drivers/mmc/tmio-common.c | 11 | ||||
-rw-r--r-- | drivers/mmc/tmio-common.h | 1 | ||||
-rw-r--r-- | drivers/mmc/uniphier-sd.c | 1 |
7 files changed, 137 insertions, 72 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 84d157ff403..b04345a1e15 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -724,7 +724,8 @@ static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) return err; } -int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) +static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, + bool send_status) { struct mmc_cmd cmd; int timeout = 1000; @@ -740,19 +741,29 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) while (retries > 0) { ret = mmc_send_cmd(mmc, &cmd, NULL); - /* Waiting for the ready status */ - if (!ret) { - ret = mmc_send_status(mmc, timeout); - return ret; + if (ret) { + retries--; + continue; + } + + if (!send_status) { + mdelay(50); + return 0; } - retries--; + /* Waiting for the ready status */ + return mmc_send_status(mmc, timeout); } return ret; } +int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) +{ + return __mmc_switch(mmc, set, index, value, true); +} + #if !CONFIG_IS_ENABLED(MMC_TINY) static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode, bool hsdowngrade) @@ -784,8 +795,9 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode, default: return -EINVAL; } - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, - speed_bits); + + err = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, + speed_bits, !hsdowngrade); if (err) return err; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 5cb97eb02af..826a39fad72 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -47,6 +47,7 @@ #endif #include <dm.h> #include <power/regulator.h> +#include <thermal.h> DECLARE_GLOBAL_DATA_PTR; @@ -470,21 +471,21 @@ static int omap_hsmmc_set_io_regulator(struct mmc *mmc, int mV) return 0; /* Disable PBIAS */ - ret = regulator_set_enable(priv->pbias_supply, false); - if (ret && ret != -ENOSYS) + ret = regulator_set_enable_if_allowed(priv->pbias_supply, false); + if (ret) return ret; /* Turn off IO voltage */ - ret = regulator_set_enable(mmc->vqmmc_supply, false); - if (ret && ret != -ENOSYS) + ret = regulator_set_enable_if_allowed(mmc->vqmmc_supply, false); + if (ret) 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) + ret = regulator_set_enable_if_allowed(mmc->vqmmc_supply, true); + if (ret) return ret; /* Program PBIAS voltage*/ @@ -492,8 +493,8 @@ static int omap_hsmmc_set_io_regulator(struct mmc *mmc, int mV) if (ret && ret != -ENOSYS) return ret; /* Enable PBIAS */ - ret = regulator_set_enable(priv->pbias_supply, true); - if (ret && ret != -ENOSYS) + ret = regulator_set_enable_if_allowed(priv->pbias_supply, true); + if (ret) return ret; return 0; @@ -622,6 +623,10 @@ static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) u32 phase_delay = 0; u32 start_window = 0, max_window = 0; u32 length = 0, max_len = 0; + bool single_point_failure = false; + struct udevice *thermal_dev; + int temperature; + int i; mmc_base = priv->base_addr; val = readl(&mmc_base->capa2); @@ -632,9 +637,25 @@ static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) ((mmc->selected_mode == UHS_SDR50) && (val & CAPA2_TSDR50)))) return 0; + ret = uclass_first_device(UCLASS_THERMAL, &thermal_dev); + if (ret) { + printf("Couldn't get thermal device for tuning\n"); + return ret; + } + ret = thermal_get_temp(thermal_dev, &temperature); + if (ret) { + printf("Couldn't get temperature for tuning\n"); + return ret; + } val = readl(&mmc_base->dll); val |= DLL_SWT; writel(val, &mmc_base->dll); + + /* + * Stage 1: Search for a maximum pass window ignoring any + * any single point failures. If the tuning value ends up + * near it, move away from it in stage 2 below + */ while (phase_delay <= MAX_PHASE_DELAY) { omap_hsmmc_set_dll(mmc, phase_delay); @@ -643,10 +664,16 @@ static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) if (cur_match) { if (prev_match) { length++; + } else if (single_point_failure) { + /* ignore single point failure */ + length++; + single_point_failure = false; } else { start_window = phase_delay; length = 1; } + } else { + single_point_failure = prev_match; } if (length > max_len) { @@ -668,8 +695,71 @@ static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) ret = -EIO; goto tuning_error; } + /* + * Assign tuning value as a ratio of maximum pass window based + * on temperature + */ + if (temperature < -20000) + phase_delay = min(max_window + 4 * max_len - 24, + max_window + + DIV_ROUND_UP(13 * max_len, 16) * 4); + else if (temperature < 20000) + phase_delay = max_window + DIV_ROUND_UP(9 * max_len, 16) * 4; + else if (temperature < 40000) + phase_delay = max_window + DIV_ROUND_UP(8 * max_len, 16) * 4; + else if (temperature < 70000) + phase_delay = max_window + DIV_ROUND_UP(7 * max_len, 16) * 4; + else if (temperature < 90000) + phase_delay = max_window + DIV_ROUND_UP(5 * max_len, 16) * 4; + else if (temperature < 120000) + phase_delay = max_window + DIV_ROUND_UP(4 * max_len, 16) * 4; + else + phase_delay = max_window + DIV_ROUND_UP(3 * max_len, 16) * 4; + + /* + * Stage 2: Search for a single point failure near the chosen tuning + * value in two steps. First in the +3 to +10 range and then in the + * +2 to -10 range. If found, move away from it in the appropriate + * direction by the appropriate amount depending on the temperature. + */ + for (i = 3; i <= 10; i++) { + omap_hsmmc_set_dll(mmc, phase_delay + i); + if (mmc_send_tuning(mmc, opcode, NULL)) { + if (temperature < 10000) + phase_delay += i + 6; + else if (temperature < 20000) + phase_delay += i - 12; + else if (temperature < 70000) + phase_delay += i - 8; + else if (temperature < 90000) + phase_delay += i - 6; + else + phase_delay += i - 6; + + goto single_failure_found; + } + } + + for (i = 2; i >= -10; i--) { + omap_hsmmc_set_dll(mmc, phase_delay + i); + if (mmc_send_tuning(mmc, opcode, NULL)) { + if (temperature < 10000) + phase_delay += i + 12; + else if (temperature < 20000) + phase_delay += i + 8; + else if (temperature < 70000) + phase_delay += i + 8; + else if (temperature < 90000) + phase_delay += i + 10; + else + phase_delay += i + 12; + + goto single_failure_found; + } + } + +single_failure_found: - phase_delay = max_window + 4 * ((3 * max_len) >> 2); omap_hsmmc_set_dll(mmc, phase_delay); mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index 733b6d62f5d..a556acd5cb5 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -462,6 +462,16 @@ static void renesas_sdhi_filter_caps(struct udevice *dev) priv->nrtaps = 4; else priv->nrtaps = 8; + + /* H3 ES1.x and M3W ES1.0 uses bit 17 for DTRAEND */ + if (((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && + (rmobile_get_cpu_rev_integer() <= 1)) || + ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && + (rmobile_get_cpu_rev_integer() == 1) && + (rmobile_get_cpu_rev_fraction() == 0))) + priv->read_poll_flag = TMIO_SD_DMA_INFO1_END_RD; + else + priv->read_poll_flag = TMIO_SD_DMA_INFO1_END_RD2; } static int renesas_sdhi_probe(struct udevice *dev) diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 591a3bce084..9dd0b865eb8 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -118,9 +118,6 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) return s5p_sdhci_core_init(host); } -#if CONFIG_IS_ENABLED(OF_CONTROL) -struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; - static int do_sdhci_init(struct sdhci_host *host) { int dev_id, flag, ret; @@ -191,53 +188,6 @@ static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) return 0; } -static int process_nodes(const void *blob, int node_list[], int count) -{ - struct sdhci_host *host; - int i, node, ret; - int failed = 0; - - debug("%s: count = %d\n", __func__, count); - - /* build sdhci_host[] for each controller */ - for (i = 0; i < count; i++) { - node = node_list[i]; - if (node <= 0) - continue; - - host = &sdhci_host[i]; - - ret = sdhci_get_config(blob, node, host); - if (ret) { - printf("%s: failed to decode dev %d (%d)\n", __func__, i, ret); - failed++; - continue; - } - - ret = do_sdhci_init(host); - if (ret && ret != -ENODEV) { - printf("%s: failed to initialize dev %d (%d)\n", __func__, i, ret); - failed++; - } - } - - /* we only consider it an error when all nodes fail */ - return (failed == count ? -1 : 0); -} - -int exynos_mmc_init(const void *blob) -{ - int count; - int node_list[SDHCI_MAX_HOSTS]; - - count = fdtdec_find_aliases_for_id(blob, "mmc", - COMPAT_SAMSUNG_EXYNOS_MMC, node_list, - SDHCI_MAX_HOSTS); - - return process_nodes(blob, node_list, count); -} -#endif - #ifdef CONFIG_DM_MMC static int s5p_sdhci_probe(struct udevice *dev) { diff --git a/drivers/mmc/tmio-common.c b/drivers/mmc/tmio-common.c index 201492001f5..2421915a079 100644 --- a/drivers/mmc/tmio-common.c +++ b/drivers/mmc/tmio-common.c @@ -347,12 +347,10 @@ static int tmio_sd_dma_xfer(struct udevice *dev, struct mmc_data *data) /* * The DMA READ completion flag position differs on Socionext * and Renesas SoCs. It is bit 20 on Socionext SoCs and using - * bit 17 is a hardware bug and forbidden. It is bit 17 on - * Renesas SoCs and bit 20 does not work on them. + * bit 17 is a hardware bug and forbidden. It is either bit 17 + * or bit 20 on Renesas SoCs, depending on SoC. */ - poll_flag = (priv->caps & TMIO_SD_CAP_RCAR) ? - TMIO_SD_DMA_INFO1_END_RD : - TMIO_SD_DMA_INFO1_END_RD2; + poll_flag = priv->read_poll_flag; tmp |= TMIO_SD_DMA_MODE_DIR_RD; } else { buf = (void *)data->src; @@ -369,6 +367,9 @@ static int tmio_sd_dma_xfer(struct udevice *dev, struct mmc_data *data) ret = tmio_sd_dma_wait_for_irq(dev, poll_flag, data->blocks); + if (poll_flag == TMIO_SD_DMA_INFO1_END_RD) + udelay(1); + __dma_unmap_single(dma_addr, len, dir); return ret; diff --git a/drivers/mmc/tmio-common.h b/drivers/mmc/tmio-common.h index 192026ce3ee..58ce3d65b02 100644 --- a/drivers/mmc/tmio-common.h +++ b/drivers/mmc/tmio-common.h @@ -119,6 +119,7 @@ struct tmio_sd_priv { void __iomem *regbase; unsigned int version; u32 caps; + u32 read_poll_flag; #define TMIO_SD_CAP_NONREMOVABLE BIT(0) /* Nonremovable e.g. eMMC */ #define TMIO_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ #define TMIO_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 6539880ab5d..8f89bda2331 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -47,6 +47,7 @@ static int uniphier_sd_probe(struct udevice *dev) struct tmio_sd_priv *priv = dev_get_priv(dev); priv->clk_get_rate = uniphier_sd_clk_get_rate; + priv->read_poll_flag = TMIO_SD_DMA_INFO1_END_RD2; #ifndef CONFIG_SPL_BUILD int ret; |