feat(nxp-mmc): add timeout to blocking loops
Replace all register polling loops with timeout-enabled versions,
allowing the caller to decide whether to panic or handle the error
gracefully.
Change-Id: If296f06a20cb849e690d1193bda1378508d3f7e0
Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
diff --git a/drivers/imx/usdhc/imx_usdhc.c b/drivers/imx/usdhc/imx_usdhc.c
index 30caeba..7168679 100644
--- a/drivers/imx/usdhc/imx_usdhc.c
+++ b/drivers/imx/usdhc/imx_usdhc.c
@@ -14,7 +14,7 @@
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <drivers/mmc.h>
-#include <lib/mmio.h>
+#include <lib/mmio_poll.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <imx_usdhc.h>
@@ -24,6 +24,9 @@
BIT_32(24U) | BIT_32(25U))
#define ADTC_MASK_ACMD (BIT_64(51U))
+#define USDHC_TIMEOUT_US (1U * 1000U) /* 1 msec */
+#define USDHC_TRANSFER_TIMEOUT (1U * 1000U * 1000U) /* 1 sec */
+
struct imx_usdhc_device_data {
uint32_t addr;
uint32_t blk_size;
@@ -105,11 +108,13 @@
}
#define IMX7_MMC_SRC_CLK_RATE (200 * 1000 * 1000)
-static void imx_usdhc_set_clk(unsigned int clk)
+static int imx_usdhc_set_clk(unsigned int clk)
{
unsigned int sdhc_clk = IMX7_MMC_SRC_CLK_RATE;
uintptr_t reg_base = imx_usdhc_params.reg_base;
unsigned int pre_div = 1U, div = 1U;
+ uint32_t pstate;
+ int ret;
assert(clk > 0);
@@ -124,7 +129,12 @@
div -= 1;
clk = (pre_div << 8) | (div << 4);
- while ((mmio_read_32(reg_base + PSTATE) & PSTATE_SDSTB) == 0U) {
+ ret = mmio_read_32_poll_timeout(reg_base + PSTATE, pstate,
+ (pstate & PSTATE_SDSTB) != 0U,
+ USDHC_TIMEOUT_US);
+ if (ret == -ETIMEDOUT) {
+ ERROR("Unstable SD clock\n");
+ return ret;
}
mmio_clrbits32(reg_base + VENDSPEC, VENDSPEC_CARD_CLKEN);
@@ -132,12 +142,15 @@
udelay(10000);
mmio_setbits32(reg_base + VENDSPEC, VENDSPEC_PER_CLKEN | VENDSPEC_CARD_CLKEN);
+
+ return 0;
}
static void imx_usdhc_initialize(void)
{
- unsigned int timeout = 10000;
uintptr_t reg_base = imx_usdhc_params.reg_base;
+ uint32_t sysctrl;
+ int ret;
assert((imx_usdhc_params.reg_base & MMC_BLOCK_MASK) == 0);
@@ -145,10 +158,12 @@
mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTA);
/* wait for reset done */
- while ((mmio_read_32(reg_base + SYSCTRL) & SYSCTRL_RSTA)) {
- if (!timeout)
- ERROR("IMX MMC reset timeout.\n");
- timeout--;
+ ret = mmio_read_32_poll_timeout(reg_base + SYSCTRL, sysctrl,
+ (sysctrl & SYSCTRL_RSTA) == 0U,
+ USDHC_TIMEOUT_US);
+ if (ret == -ETIMEDOUT) {
+ ERROR("Failed to reset the USDHC controller\n");
+ panic();
}
mmio_write_32(reg_base + MMCBOOT, 0);
@@ -160,7 +175,11 @@
mmio_setbits32(reg_base + VENDSPEC, VENDSPEC_IPG_CLKEN | VENDSPEC_PER_CLKEN);
/* Set the initial boot clock rate */
- imx_usdhc_set_clk(MMC_BOOT_CLK_RATE);
+ ret = imx_usdhc_set_clk(MMC_BOOT_CLK_RATE);
+ if (ret != 0) {
+ panic();
+ }
+
udelay(100);
/* Clear read/write ready status */
@@ -177,8 +196,6 @@
mmio_clrsetbits32(reg_base + WATERMARKLEV, WMKLV_MASK, 16 | (16 << 16));
}
-#define FSL_CMD_RETRIES 1000
-
static bool is_data_transfer_to_card(const struct mmc_cmd *cmd)
{
unsigned int cmd_idx = cmd->cmd_idx;
@@ -244,12 +261,11 @@
static int imx_usdhc_send_cmd(struct mmc_cmd *cmd)
{
uintptr_t reg_base = imx_usdhc_params.reg_base;
- unsigned int state, flags = INTSTATEN_CC | INTSTATEN_CTOE;
+ unsigned int flags = INTSTATEN_CC | INTSTATEN_CTOE;
+ uint32_t xfertype, pstate, intstat, sysctrl;
unsigned int mixctl = 0;
- unsigned int cmd_retries = 0;
- uint32_t xfertype;
+ int err = 0, ret;
bool data;
- int err = 0;
assert(cmd);
@@ -264,12 +280,21 @@
mmio_write_32(reg_base + INTSTAT, 0xffffffff);
/* Wait for the bus to be idle */
- do {
- state = mmio_read_32(reg_base + PSTATE);
- } while (state & (PSTATE_CDIHB | PSTATE_CIHB));
+ err = mmio_read_32_poll_timeout(reg_base + PSTATE, pstate,
+ (pstate & (PSTATE_CDIHB | PSTATE_CIHB)) == 0U,
+ USDHC_TIMEOUT_US);
+ if (err == -ETIMEDOUT) {
+ ERROR("Failed to wait an idle bus\n");
+ return err;
+ }
- while (mmio_read_32(reg_base + PSTATE) & PSTATE_DLA)
- ;
+ err = mmio_read_32_poll_timeout(reg_base + PSTATE, pstate,
+ (pstate & PSTATE_DLA) == 0U,
+ USDHC_TIMEOUT_US);
+ if (err == -ETIMEDOUT) {
+ ERROR("Active data line during the uSDHC init\n");
+ return err;
+ }
mmio_write_32(reg_base + INTSIGEN, 0);
@@ -296,19 +321,15 @@
mmio_write_32(reg_base + XFERTYPE, xfertype);
/* Wait for the command done */
- do {
- state = mmio_read_32(reg_base + INTSTAT);
- if (cmd_retries)
- udelay(1);
- } while ((!(state & flags)) && ++cmd_retries < FSL_CMD_RETRIES);
-
- if ((state & (INTSTATEN_CTOE | CMD_ERR)) || cmd_retries == FSL_CMD_RETRIES) {
- if (cmd_retries == FSL_CMD_RETRIES)
- err = -ETIMEDOUT;
- else
+ err = mmio_read_32_poll_timeout(reg_base + INTSTAT, intstat,
+ (intstat & flags) != 0U,
+ USDHC_TIMEOUT_US);
+ if ((err == -ETIMEDOUT) || ((intstat & (INTSTATEN_CTOE | CMD_ERR)) != 0U)) {
+ if ((intstat & (INTSTATEN_CTOE | CMD_ERR)) != 0U) {
err = -EIO;
+ }
ERROR("imx_usdhc mmc cmd %d state 0x%x errno=%d\n",
- cmd->cmd_idx, state, err);
+ cmd->cmd_idx, intstat, err);
goto out;
}
@@ -331,28 +352,41 @@
/* Wait until all of the blocks are transferred */
if (data) {
flags = DATA_COMPLETE;
- do {
- state = mmio_read_32(reg_base + INTSTAT);
+ err = mmio_read_32_poll_timeout(reg_base + INTSTAT, intstat,
+ (((intstat & (INTSTATEN_DTOE | DATA_ERR)) != 0U) ||
+ ((intstat & flags) == flags)),
+ USDHC_TRANSFER_TIMEOUT);
+ if ((intstat & (INTSTATEN_DTOE | DATA_ERR)) != 0U) {
+ err = -EIO;
+ ERROR("imx_usdhc mmc data state 0x%x\n", intstat);
+ goto out;
+ }
- if (state & (INTSTATEN_DTOE | DATA_ERR)) {
- err = -EIO;
- ERROR("imx_usdhc mmc data state 0x%x\n", state);
- goto out;
- }
- } while ((state & flags) != flags);
+ if (err == -ETIMEDOUT) {
+ ERROR("Timeout in block transfer\n");
+ goto out;
+ }
}
out:
/* Reset CMD and DATA on error */
if (err) {
mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTC);
- while (mmio_read_32(reg_base + SYSCTRL) & SYSCTRL_RSTC)
- ;
+ ret = mmio_read_32_poll_timeout(reg_base + SYSCTRL, sysctrl,
+ (sysctrl & SYSCTRL_RSTC) == 0U,
+ USDHC_TIMEOUT_US);
+ if (ret == -ETIMEDOUT) {
+ ERROR("Failed to reset the CMD line\n");
+ }
if (data) {
mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTD);
- while (mmio_read_32(reg_base + SYSCTRL) & SYSCTRL_RSTD)
- ;
+ ret = mmio_read_32_poll_timeout(reg_base + SYSCTRL, sysctrl,
+ (sysctrl & SYSCTRL_RSTD) == 0U,
+ USDHC_TIMEOUT_US);
+ if (ret == -ETIMEDOUT) {
+ ERROR("Failed to reset the data line\n");
+ }
}
}
@@ -365,8 +399,12 @@
static int imx_usdhc_set_ios(unsigned int clk, unsigned int width)
{
uintptr_t reg_base = imx_usdhc_params.reg_base;
+ int ret;
- imx_usdhc_set_clk(clk);
+ ret = imx_usdhc_set_clk(clk);
+ if (ret != 0) {
+ return ret;
+ }
if (width == MMC_BUS_WIDTH_4)
mmio_clrsetbits32(reg_base + PROTCTRL, PROTCTRL_WIDTH_MASK,