summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/mmci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/mmci.c')
-rw-r--r--drivers/mmc/host/mmci.c39
1 files changed, 27 insertions, 12 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 4ab0c622dff3..4c78783e5e1a 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -674,6 +674,7 @@ static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host)
static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
{
void __iomem *base = host->base;
+ int retries = 10;
if (status & err_msk) {
/* Stop any ongoing busy detection if an error occurs */
@@ -694,21 +695,35 @@ static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk)
* Note that, the card may need a couple of clock cycles before
* it starts signaling busy on DAT0, hence re-read the
* MMCISTATUS register here, to allow the busy bit to be set.
- * Potentially we may even need to poll the register for a
- * while, to allow it to be set, but tests indicates that it
- * isn't needed.
*/
if (host->busy_state == MMCI_BUSY_DONE) {
- status = readl(base + MMCISTATUS);
- if (status & host->variant->busy_detect_flag) {
- writel(readl(base + MMCIMASK0) |
- host->variant->busy_detect_mask,
- base + MMCIMASK0);
-
- host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
- host->busy_state = MMCI_BUSY_WAITING_FOR_START_IRQ;
- return false;
+ /*
+ * Save the first status register read to be sure to catch
+ * all bits that may be lost will retrying. If the command
+ * is still busy this will result in assigning 0 to
+ * host->busy_status, which is what it should be in IDLE.
+ */
+ host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND);
+ while (retries) {
+ status = readl(base + MMCISTATUS);
+ /* Keep accumulating status bits */
+ host->busy_status |= status & (MCI_CMDSENT | MCI_CMDRESPEND);
+ if (status & host->variant->busy_detect_flag) {
+ writel(readl(base + MMCIMASK0) |
+ host->variant->busy_detect_mask,
+ base + MMCIMASK0);
+ host->busy_state = MMCI_BUSY_WAITING_FOR_START_IRQ;
+ return false;
+ }
+ retries--;
}
+ dev_dbg(mmc_dev(host->mmc), "no busy signalling in time\n");
+ writel(host->variant->busy_detect_mask, base + MMCICLEAR);
+ writel(readl(base + MMCIMASK0) &
+ ~host->variant->busy_detect_mask, base + MMCIMASK0);
+ host->busy_state = MMCI_BUSY_DONE;
+ host->busy_status = 0;
+ return true;
}
/*