diff mbox

[RFC] mmc: dw_mmc: mark card as removed if error occurs and status is busy

Message ID 1424102861-30064-1-git-send-email-a.hajda@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrzej Hajda Feb. 16, 2015, 4:07 p.m. UTC
On some boards with broken card detection if eMMC card is not present system
hangs during mmc_rescan. The sequence is as below:
mmc_rescan_try_freq:sdio_reset:
  > send CMD52
  < irq with SDMMC_INT_RESP_ERR and SDMMC_STATUS_BUSY
  < irq with SDMMC_INT_CMD_DONE and SDMMC_STATUS_BUSY
  > dw_mci_wait_busy:dw_mci_ctrl_reset
  < irq with SDMMC_INT_RESP_ERR | SDMMC_INT_CMD_DONE and status not busy
  > send CMD52
  ... mmc_rescan waits infinitely for irq in mmc_wait_for_req

It seems hardware enters into some strange state after issuing CMD52 to
non-existing card. It stops signaling IRQs and it can even hang on accessing
some registers, for example UHS_REG or CTYPE.
The patch tries to solve it by resetting the controller and marking card
as removed, as a result no further commands will be issued until next rescan.

The issue has been observed on Odroid-XU3 boards (Exynos5422 with dw_mmc 250A).

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
---
Hi,

I am not familiar with MMC internals so please verify patch (in-)sanity.
Especially I am not sure if (SDMMC_INT_RESP_ERR | SDMMC_INT_CMD_DONE with
status busy) occurs only in this situation, if no, more precise check should
be added, I guess. Or maybe it should be a quirk???

The patch is based on Addy's 'about data busy' patchset [1].
Alim's advice about stabilization of host voltage has been tested also,
without success [2].

[1]: http://permalink.gmane.org/gmane.linux.kernel/1888161
[2]: http://permalink.gmane.org/gmane.linux.kernel.mmc/31202

Regards
Andrzej
---
 drivers/mmc/host/dw_mmc.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 692d97a..3cba1eb 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2182,8 +2182,19 @@  static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 		}
 
 		if (pending & SDMMC_INT_CMD_DONE) {
+			u32 err = (pending | host->cmd_status) &
+				SDMMC_INT_RESP_ERR;
+			struct dw_mci_slot *slot = host->cur_slot;
+
 			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
-			dw_mci_cmd_interrupt(host, pending);
+			if (err && dw_mci_card_busy(slot->mmc)) {
+				u32 ctrl = mci_readl(host, CTRL);
+
+				ctrl |= SDMMC_CTRL_ALL_RESET_FLAGS;
+				mci_writel(host, CTRL, ctrl);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			} else
+				dw_mci_cmd_interrupt(host, pending);
 		}
 
 		if (pending & SDMMC_INT_CD) {