Commit 41005003 authored by Russell King's avatar Russell King Committed by Chris Ball

mmc: sdhci: clean up interrupt handling

sdhci interrupt handling is a mess; there is a lot of code doing very
similar things.  Let's clean this up a bit:

1. set's clear down cmd, data and bus power interrupts in one go - we're
   always going to handle these.
2. use a do { } while () loop for looping while there are pending
   interrupts.
3. group clearing of bits in intmask into one place.

This results in the code becoming simpler and easier to read.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Tested-by: default avatarMarkus Pargmann <mpa@pengutronix.de>
Tested-by: default avatarStephen Warren <swarren@nvidia.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarChris Ball <chris@printf.net>
parent bf3b5ec6
...@@ -2431,7 +2431,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -2431,7 +2431,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
{ {
irqreturn_t result; irqreturn_t result;
struct sdhci_host *host = dev_id; struct sdhci_host *host = dev_id;
u32 intmask, unexpected = 0; u32 intmask, mask, unexpected = 0;
int cardint = 0, max_loops = 16; int cardint = 0, max_loops = 16;
spin_lock(&host->lock); spin_lock(&host->lock);
...@@ -2442,88 +2442,80 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -2442,88 +2442,80 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
} }
intmask = sdhci_readl(host, SDHCI_INT_STATUS); intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) { if (!intmask || intmask == 0xffffffff) {
result = IRQ_NONE; result = IRQ_NONE;
goto out; goto out;
} }
again: do {
DBG("*** %s got interrupt: 0x%08x\n", /* Clear selected interrupts. */
mmc_hostname(host->mmc), intmask); mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
SDHCI_INT_BUS_POWER);
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { sdhci_writel(host, mask, SDHCI_INT_STATUS);
u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT;
/*
* There is a observation on i.mx esdhc. INSERT bit will be
* immediately set again when it gets cleared, if a card is
* inserted. We have to mask the irq to prevent interrupt
* storm which will freeze the system. And the REMOVE gets
* the same situation.
*
* More testing are needed here to ensure it works for other
* platforms though.
*/
sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
SDHCI_INT_CARD_REMOVE);
sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
SDHCI_INT_CARD_INSERT);
sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
tasklet_schedule(&host->card_tasklet);
}
if (intmask & SDHCI_INT_CMD_MASK) { DBG("*** %s got interrupt: 0x%08x\n",
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, mmc_hostname(host->mmc), intmask);
SDHCI_INT_STATUS);
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
}
if (intmask & SDHCI_INT_DATA_MASK) { if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK, u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_INT_STATUS); SDHCI_CARD_PRESENT;
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
}
intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); /*
* There is a observation on i.mx esdhc. INSERT
* bit will be immediately set again when it gets
* cleared, if a card is inserted. We have to mask
* the irq to prevent interrupt storm which will
* freeze the system. And the REMOVE gets the
* same situation.
*
* More testing are needed here to ensure it works
* for other platforms though.
*/
sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
SDHCI_INT_CARD_REMOVE);
sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
SDHCI_INT_CARD_INSERT);
sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
tasklet_schedule(&host->card_tasklet);
}
intmask &= ~SDHCI_INT_ERROR; if (intmask & SDHCI_INT_CMD_MASK)
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
if (intmask & SDHCI_INT_BUS_POWER) { if (intmask & SDHCI_INT_DATA_MASK)
pr_err("%s: Card is consuming too much power!\n", sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
mmc_hostname(host->mmc));
sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
}
intmask &= ~SDHCI_INT_BUS_POWER; if (intmask & SDHCI_INT_BUS_POWER)
pr_err("%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
if (intmask & SDHCI_INT_CARD_INT) if (intmask & SDHCI_INT_CARD_INT)
cardint = 1; cardint = 1;
intmask &= ~SDHCI_INT_CARD_INT; intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
SDHCI_INT_CARD_INT);
if (intmask) { if (intmask) {
unexpected |= intmask; unexpected |= intmask;
sdhci_writel(host, intmask, SDHCI_INT_STATUS); sdhci_writel(host, intmask, SDHCI_INT_STATUS);
} }
result = IRQ_HANDLED; result = IRQ_HANDLED;
intmask = sdhci_readl(host, SDHCI_INT_STATUS); intmask = sdhci_readl(host, SDHCI_INT_STATUS);
/* /*
* If we know we'll call the driver to signal SDIO IRQ, disregard * If we know we'll call the driver to signal SDIO IRQ,
* further indications of Card Interrupt in the status to avoid a * disregard further indications of Card Interrupt in
* needless loop. * the status to avoid a needless loop.
*/ */
if (cardint) if (cardint)
intmask &= ~SDHCI_INT_CARD_INT; intmask &= ~SDHCI_INT_CARD_INT;
if (intmask && --max_loops) } while (intmask && --max_loops);
goto again;
out: out:
spin_unlock(&host->lock); spin_unlock(&host->lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment