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

mmc: sdio: Add API to manage SDIO IRQs from a workqueue

For hosts not supporting MMC_CAP2_SDIO_IRQ_NOTHREAD but MMC_CAP_SDIO_IRQ,
the SDIO IRQs are processed from a dedicated kernel thread. For these
cases, the host calls mmc_signal_sdio_irq() from its ISR to signal a new
SDIO IRQ.

Signaling an SDIO IRQ makes the host's ->enable_sdio_irq() callback to be
invoked to temporary disable the IRQs, before the kernel thread is woken up
to process it. When processing of the IRQs are completed, they are
re-enabled by the kernel thread, again via invoking the host's
->enable_sdio_irq().

The observation from this, is that the execution path is being unnecessary
complex, as the host driver already knows that it needs to temporary
disable the IRQs before signaling a new one. Moreover, replacing the kernel
thread with a work/workqueue would not only greatly simplify the code, but
also make it more robust.

To address the above problems, let's continue to build upon the support for
MMC_CAP2_SDIO_IRQ_NOTHREAD, as it already implements SDIO IRQs to be
processed without using the clumsy kernel thread and without the ping-pong
calls of the host's ->enable_sdio_irq() callback for each processed IRQ.

Therefore, let's add new API sdio_signal_irq(), which enables hosts to
signal/process SDIO IRQs by using a work/workqueue, rather than using the
kernel thread.

Add also a new host callback ->ack_sdio_irq(), which the work invokes when
the SDIO IRQs have been processed. This informs the host about when it
shall re-enable the SDIO IRQs. Potentially, we could re-use the existing
->enable_sdio_irq() callback instead of adding a new one, however it has
turned out that it's more convenient for hosts to get this information via
a separate callback.

Hosts that wants to use this new method to signal/process SDIO IRQs, must
enable MMC_CAP2_SDIO_IRQ_NOTHREAD and implement the ->ack_sdio_irq()
callback.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>

+23
+2
drivers/mmc/core/host.c
··· 30 30 #include "host.h" 31 31 #include "slot-gpio.h" 32 32 #include "pwrseq.h" 33 + #include "sdio_ops.h" 33 34 34 35 #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) 35 36 ··· 380 379 spin_lock_init(&host->lock); 381 380 init_waitqueue_head(&host->wq); 382 381 INIT_DELAYED_WORK(&host->detect, mmc_rescan); 382 + INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work); 383 383 setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host); 384 384 385 385 /*
+16
drivers/mmc/core/sdio_irq.c
··· 98 98 if (host->sdio_irqs) { 99 99 host->sdio_irq_pending = true; 100 100 process_sdio_pending_irqs(host); 101 + if (host->ops->ack_sdio_irq) 102 + host->ops->ack_sdio_irq(host); 101 103 } 102 104 mmc_release_host(host); 103 105 } 104 106 EXPORT_SYMBOL_GPL(sdio_run_irqs); 107 + 108 + void sdio_irq_work(struct work_struct *work) 109 + { 110 + struct mmc_host *host = 111 + container_of(work, struct mmc_host, sdio_irq_work.work); 112 + 113 + sdio_run_irqs(host); 114 + } 115 + 116 + void sdio_signal_irq(struct mmc_host *host) 117 + { 118 + queue_delayed_work(system_wq, &host->sdio_irq_work, 0); 119 + } 120 + EXPORT_SYMBOL_GPL(sdio_signal_irq); 105 121 106 122 static int sdio_irq_thread(void *_host) 107 123 {
+2
drivers/mmc/core/sdio_ops.h
··· 17 17 18 18 struct mmc_host; 19 19 struct mmc_card; 20 + struct work_struct; 20 21 21 22 int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); 22 23 int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ··· 26 25 unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz); 27 26 int sdio_reset(struct mmc_host *host); 28 27 unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz); 28 + void sdio_irq_work(struct work_struct *work); 29 29 30 30 static inline bool sdio_is_io_busy(u32 opcode, u32 arg) 31 31 {
+3
include/linux/mmc/host.h
··· 130 130 int (*get_cd)(struct mmc_host *host); 131 131 132 132 void (*enable_sdio_irq)(struct mmc_host *host, int enable); 133 + void (*ack_sdio_irq)(struct mmc_host *host); 133 134 134 135 /* optional callback for HC quirks */ 135 136 void (*init_card)(struct mmc_host *host, struct mmc_card *card); ··· 359 358 360 359 unsigned int sdio_irqs; 361 360 struct task_struct *sdio_irq_thread; 361 + struct delayed_work sdio_irq_work; 362 362 bool sdio_irq_pending; 363 363 atomic_t sdio_irq_thread_abort; 364 364 ··· 430 428 } 431 429 432 430 void sdio_run_irqs(struct mmc_host *host); 431 + void sdio_signal_irq(struct mmc_host *host); 433 432 434 433 #ifdef CONFIG_REGULATOR 435 434 int mmc_regulator_get_ocrmask(struct regulator *supply);