summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-02-10 14:35:22 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2017-02-10 14:35:22 -0800
commitfc6f41ba8b2e705f91324db158c3cc28209a15b1 (patch)
tree567bede1cc6bd9f2dec3a3dc85e908e02557da2e
parent1f369d1655c1de415a186c6ce9004e40ca790989 (diff)
parent5cad24d835772f9f709971a8d6fcf12afe53b2a7 (diff)
Merge tag 'mmc-v4.10-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC host fix from Ulf Hansson: "mmci: Fix hang while waiting for busy-end interrupt" * tag 'mmc-v4.10-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: mmc: mmci: avoid clearing ST Micro busy end interrupt mistakenly
-rw-r--r--drivers/mmc/host/mmci.c32
1 files changed, 25 insertions, 7 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 01a804792f30..b5972440c1bf 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1023,7 +1023,12 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
if (!host->busy_status && busy_resp &&
!(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
(readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
- /* Unmask the busy IRQ */
+
+ /* Clear the busy start IRQ */
+ writel(host->variant->busy_detect_mask,
+ host->base + MMCICLEAR);
+
+ /* Unmask the busy end IRQ */
writel(readl(base + MMCIMASK0) |
host->variant->busy_detect_mask,
base + MMCIMASK0);
@@ -1038,10 +1043,14 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
/*
* At this point we are not busy with a command, we have
- * not received a new busy request, mask the busy IRQ and
- * fall through to process the IRQ.
+ * not received a new busy request, clear and mask the busy
+ * end IRQ and fall through to process the IRQ.
*/
if (host->busy_status) {
+
+ writel(host->variant->busy_detect_mask,
+ host->base + MMCICLEAR);
+
writel(readl(base + MMCIMASK0) &
~host->variant->busy_detect_mask,
base + MMCIMASK0);
@@ -1283,12 +1292,21 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
}
/*
- * We intentionally clear the MCI_ST_CARDBUSY IRQ here (if it's
- * enabled) since the HW seems to be triggering the IRQ on both
- * edges while monitoring DAT0 for busy completion.
+ * We intentionally clear the MCI_ST_CARDBUSY IRQ (if it's
+ * enabled) in mmci_cmd_irq() function where ST Micro busy
+ * detection variant is handled. Considering the HW seems to be
+ * triggering the IRQ on both edges while monitoring DAT0 for
+ * busy completion and that same status bit is used to monitor
+ * start and end of busy detection, special care must be taken
+ * to make sure that both start and end interrupts are always
+ * cleared one after the other.
*/
status &= readl(host->base + MMCIMASK0);
- writel(status, host->base + MMCICLEAR);
+ if (host->variant->busy_detect)
+ writel(status & ~host->variant->busy_detect_mask,
+ host->base + MMCICLEAR);
+ else
+ writel(status, host->base + MMCICLEAR);
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);