Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: sdio_irq: rework sdio irq handling

Rather than the SDIO support spawning it's own thread for handling card
interrupts, use the generic IRQ infrastructure for this, triggering it
from the host interface's interrupt handling directly.

This avoids a race between the parent thread waiting to receive an
interrupt response from the card, and the slow startup from the sdio
irq thread, which can occur as a result of high system load (eg, while
udev is running.)

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>
[Ulf Hansson] Resolved conflict
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Chris Ball <chris@printf.net>

authored by

Russell King and committed by
Chris Ball
bf3b5ec6 197160d5

+34 -10
+31 -10
drivers/mmc/core/sdio_irq.c
··· 90 90 return ret; 91 91 } 92 92 93 + void sdio_run_irqs(struct mmc_host *host) 94 + { 95 + mmc_claim_host(host); 96 + host->sdio_irq_pending = true; 97 + process_sdio_pending_irqs(host); 98 + mmc_release_host(host); 99 + } 100 + EXPORT_SYMBOL_GPL(sdio_run_irqs); 101 + 93 102 static int sdio_irq_thread(void *_host) 94 103 { 95 104 struct mmc_host *host = _host; ··· 198 189 WARN_ON(!host->claimed); 199 190 200 191 if (!host->sdio_irqs++) { 201 - atomic_set(&host->sdio_irq_thread_abort, 0); 202 - host->sdio_irq_thread = 203 - kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", 204 - mmc_hostname(host)); 205 - if (IS_ERR(host->sdio_irq_thread)) { 206 - int err = PTR_ERR(host->sdio_irq_thread); 207 - host->sdio_irqs--; 208 - return err; 192 + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { 193 + atomic_set(&host->sdio_irq_thread_abort, 0); 194 + host->sdio_irq_thread = 195 + kthread_run(sdio_irq_thread, host, 196 + "ksdioirqd/%s", mmc_hostname(host)); 197 + if (IS_ERR(host->sdio_irq_thread)) { 198 + int err = PTR_ERR(host->sdio_irq_thread); 199 + host->sdio_irqs--; 200 + return err; 201 + } 202 + } else { 203 + mmc_host_clk_hold(host); 204 + host->ops->enable_sdio_irq(host, 1); 205 + mmc_host_clk_release(host); 209 206 } 210 207 } 211 208 ··· 226 211 BUG_ON(host->sdio_irqs < 1); 227 212 228 213 if (!--host->sdio_irqs) { 229 - atomic_set(&host->sdio_irq_thread_abort, 1); 230 - kthread_stop(host->sdio_irq_thread); 214 + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { 215 + atomic_set(&host->sdio_irq_thread_abort, 1); 216 + kthread_stop(host->sdio_irq_thread); 217 + } else { 218 + mmc_host_clk_hold(host); 219 + host->ops->enable_sdio_irq(host, 0); 220 + mmc_host_clk_release(host); 221 + } 231 222 } 232 223 233 224 return 0;
+3
include/linux/mmc/host.h
··· 282 282 #define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */ 283 283 #define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \ 284 284 MMC_CAP2_HS400_1_2V) 285 + #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17) 285 286 286 287 mmc_pm_flag_t pm_caps; /* supported pm features */ 287 288 ··· 397 396 host->sdio_irq_pending = true; 398 397 wake_up_process(host->sdio_irq_thread); 399 398 } 399 + 400 + void sdio_run_irqs(struct mmc_host *host); 400 401 401 402 #ifdef CONFIG_REGULATOR 402 403 int mmc_regulator_get_ocrmask(struct regulator *supply);