summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/mmc.c28
-rw-r--r--drivers/mmc/omap_hsmmc.c108
-rw-r--r--drivers/mmc/renesas-sdhi.c10
-rw-r--r--drivers/mmc/s5p_sdhci.c50
-rw-r--r--drivers/mmc/tmio-common.c11
-rw-r--r--drivers/mmc/tmio-common.h1
-rw-r--r--drivers/mmc/uniphier-sd.c1
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;