summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/stm32_sdmmc2.c61
1 files changed, 55 insertions, 6 deletions
diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c
index bab7efe754..ed31ca126e 100644
--- a/drivers/mmc/stm32_sdmmc2.c
+++ b/drivers/mmc/stm32_sdmmc2.c
@@ -190,6 +190,7 @@ struct stm32_sdmmc2_ctx {
#define SDMMC_IDMACTRL_IDMAEN BIT(0)
#define SDMMC_CMD_TIMEOUT 0xFFFFFFFF
+#define SDMMC_BUSYD0END_TIMEOUT_US 1000000
static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
struct mmc_data *data,
@@ -209,9 +210,6 @@ static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
idmabase0 = (u32)data->src;
}
- /* Set the SDMMC Data TimeOut value */
- writel(SDMMC_CMD_TIMEOUT, priv->base + SDMMC_DTIMER);
-
/* Set the SDMMC DataLength value */
writel(ctx->data_length, priv->base + SDMMC_DLEN);
@@ -236,8 +234,11 @@ static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
}
static void stm32_sdmmc2_start_cmd(struct stm32_sdmmc2_priv *priv,
- struct mmc_cmd *cmd, u32 cmd_param)
+ struct mmc_cmd *cmd, u32 cmd_param,
+ struct stm32_sdmmc2_ctx *ctx)
{
+ u32 timeout = 0;
+
if (readl(priv->base + SDMMC_CMD) & SDMMC_CMD_CPSMEN)
writel(0, priv->base + SDMMC_CMD);
@@ -251,6 +252,26 @@ static void stm32_sdmmc2_start_cmd(struct stm32_sdmmc2_priv *priv,
cmd_param |= SDMMC_CMD_WAITRESP_1;
}
+ /*
+ * SDMMC_DTIME must be set in two case:
+ * - on data transfert.
+ * - on busy request.
+ * If not done or too short, the dtimeout flag occurs and DPSM stays
+ * enabled/busy and waits for abort (stop transmission cmd).
+ * Next data command is not possible whereas DPSM is activated.
+ */
+ if (ctx->data_length) {
+ timeout = SDMMC_CMD_TIMEOUT;
+ } else {
+ writel(0, priv->base + SDMMC_DCTRL);
+
+ if (cmd->resp_type & MMC_RSP_BUSY)
+ timeout = SDMMC_CMD_TIMEOUT;
+ }
+
+ /* Set the SDMMC Data TimeOut value */
+ writel(timeout, priv->base + SDMMC_DTIMER);
+
/* Clear flags */
writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
@@ -309,6 +330,31 @@ static int stm32_sdmmc2_end_cmd(struct stm32_sdmmc2_priv *priv,
cmd->response[2] = readl(priv->base + SDMMC_RESP3);
cmd->response[3] = readl(priv->base + SDMMC_RESP4);
}
+
+ /* Wait for BUSYD0END flag if busy status is detected */
+ if (cmd->resp_type & MMC_RSP_BUSY &&
+ status & SDMMC_STA_BUSYD0) {
+ mask = SDMMC_STA_DTIMEOUT | SDMMC_STA_BUSYD0END;
+
+ /* Polling status register */
+ ret = readl_poll_timeout(priv->base + SDMMC_STA,
+ status, status & mask,
+ SDMMC_BUSYD0END_TIMEOUT_US);
+
+ if (ret < 0) {
+ debug("%s: timeout reading SDMMC_STA\n",
+ __func__);
+ ctx->dpsm_abort = true;
+ return ret;
+ }
+
+ if (status & SDMMC_STA_DTIMEOUT) {
+ debug("%s: error SDMMC_STA_DTIMEOUT (0x%x)\n",
+ __func__, status);
+ ctx->dpsm_abort = true;
+ return -ETIMEDOUT;
+ }
+ }
}
return 0;
@@ -395,7 +441,7 @@ retry_cmd:
stm32_sdmmc2_start_data(priv, data, &ctx);
}
- stm32_sdmmc2_start_cmd(priv, cmd, cmdat);
+ stm32_sdmmc2_start_cmd(priv, cmd, cmdat, &ctx);
debug("%s: send cmd %d data: 0x%x @ 0x%x\n",
__func__, cmd->cmdidx,
@@ -425,7 +471,10 @@ retry_cmd:
debug("%s: send STOP command to abort dpsm treatments\n",
__func__);
- stm32_sdmmc2_start_cmd(priv, &stop_cmd, SDMMC_CMD_CMDSTOP);
+ ctx.data_length = 0;
+
+ stm32_sdmmc2_start_cmd(priv, &stop_cmd,
+ SDMMC_CMD_CMDSTOP, &ctx);
stm32_sdmmc2_end_cmd(priv, &stop_cmd, &ctx);
writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);