Skip to content

Commit 781e989

Browse files
Russell Kingcjb
authored andcommitted
mmc: sdhci: convert to new SDIO IRQ handling
Use a generic threaded interrupt handler for SDIO interrupt handling, rather than allowing the SDIO core code to buggily spawn its own thread. This results in host drivers to be more in control of how SDIO interrupts are acknowledged in the hardware, rather than having the internals of the SDIO core placed upon them, possibly resulting in sub-standard handling. At least one SDHCI implementation specifies a very specific sequence to deal with a card interrupt. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Tested-by: Markus Pargmann <mpa@pengutronix.de> Tested-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Chris Ball <chris@printf.net>
1 parent ef10433 commit 781e989

File tree

2 files changed

+41
-23
lines changed

2 files changed

+41
-23
lines changed

drivers/mmc/host/sdhci.c

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,10 +2428,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
24282428

24292429
static irqreturn_t sdhci_irq(int irq, void *dev_id)
24302430
{
2431-
irqreturn_t result;
2431+
irqreturn_t result = IRQ_NONE;
24322432
struct sdhci_host *host = dev_id;
24332433
u32 intmask, mask, unexpected = 0;
2434-
int cardint = 0, max_loops = 16;
2434+
int max_loops = 16;
24352435

24362436
spin_lock(&host->lock);
24372437

@@ -2490,8 +2490,11 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
24902490
pr_err("%s: Card is consuming too much power!\n",
24912491
mmc_hostname(host->mmc));
24922492

2493-
if (intmask & SDHCI_INT_CARD_INT)
2494-
cardint = 1;
2493+
if (intmask & SDHCI_INT_CARD_INT) {
2494+
sdhci_enable_sdio_irq_nolock(host, false);
2495+
host->thread_isr |= SDHCI_INT_CARD_INT;
2496+
result = IRQ_WAKE_THREAD;
2497+
}
24952498

24962499
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
24972500
SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
@@ -2503,17 +2506,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
25032506
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
25042507
}
25052508

2506-
result = IRQ_HANDLED;
2509+
if (result == IRQ_NONE)
2510+
result = IRQ_HANDLED;
25072511

25082512
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
2509-
2510-
/*
2511-
* If we know we'll call the driver to signal SDIO IRQ,
2512-
* disregard further indications of Card Interrupt in
2513-
* the status to avoid a needless loop.
2514-
*/
2515-
if (cardint)
2516-
intmask &= ~SDHCI_INT_CARD_INT;
25172513
} while (intmask && --max_loops);
25182514
out:
25192515
spin_unlock(&host->lock);
@@ -2523,15 +2519,33 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
25232519
mmc_hostname(host->mmc), unexpected);
25242520
sdhci_dumpregs(host);
25252521
}
2526-
/*
2527-
* We have to delay this as it calls back into the driver.
2528-
*/
2529-
if (cardint)
2530-
mmc_signal_sdio_irq(host->mmc);
25312522

25322523
return result;
25332524
}
25342525

2526+
static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
2527+
{
2528+
struct sdhci_host *host = dev_id;
2529+
unsigned long flags;
2530+
u32 isr;
2531+
2532+
spin_lock_irqsave(&host->lock, flags);
2533+
isr = host->thread_isr;
2534+
host->thread_isr = 0;
2535+
spin_unlock_irqrestore(&host->lock, flags);
2536+
2537+
if (isr & SDHCI_INT_CARD_INT) {
2538+
sdio_run_irqs(host->mmc);
2539+
2540+
spin_lock_irqsave(&host->lock, flags);
2541+
if (host->flags & SDHCI_SDIO_IRQ_ENABLED)
2542+
sdhci_enable_sdio_irq_nolock(host, true);
2543+
spin_unlock_irqrestore(&host->lock, flags);
2544+
}
2545+
2546+
return isr ? IRQ_HANDLED : IRQ_NONE;
2547+
}
2548+
25352549
/*****************************************************************************\
25362550
* *
25372551
* Suspend/resume *
@@ -2601,8 +2615,9 @@ int sdhci_resume_host(struct sdhci_host *host)
26012615
}
26022616

26032617
if (!device_may_wakeup(mmc_dev(host->mmc))) {
2604-
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
2605-
mmc_hostname(host->mmc), host);
2618+
ret = request_threaded_irq(host->irq, sdhci_irq,
2619+
sdhci_thread_irq, IRQF_SHARED,
2620+
mmc_hostname(host->mmc), host);
26062621
if (ret)
26072622
return ret;
26082623
} else {
@@ -2681,7 +2696,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
26812696
sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
26822697
spin_unlock_irqrestore(&host->lock, flags);
26832698

2684-
synchronize_irq(host->irq);
2699+
synchronize_hardirq(host->irq);
26852700

26862701
spin_lock_irqsave(&host->lock, flags);
26872702
host->runtime_suspended = true;
@@ -2937,6 +2952,7 @@ int sdhci_add_host(struct sdhci_host *host)
29372952
mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
29382953

29392954
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
2955+
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
29402956

29412957
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
29422958
host->flags |= SDHCI_AUTO_CMD12;
@@ -3226,8 +3242,8 @@ int sdhci_add_host(struct sdhci_host *host)
32263242

32273243
sdhci_init(host, 0);
32283244

3229-
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
3230-
mmc_hostname(mmc), host);
3245+
ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
3246+
IRQF_SHARED, mmc_hostname(mmc), host);
32313247
if (ret) {
32323248
pr_err("%s: Failed to request IRQ %d: %d\n",
32333249
mmc_hostname(mmc), host->irq, ret);

include/linux/mmc/sdhci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ struct sdhci_host {
177177
unsigned int ocr_avail_mmc;
178178
u32 ocr_mask; /* available voltages */
179179

180+
u32 thread_isr;
181+
180182
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
181183
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
182184

0 commit comments

Comments
 (0)