mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
e3a84267ab
commit
682696605c
|
@ -30,6 +30,7 @@
|
|||
#include "host.h"
|
||||
#include "slot-gpio.h"
|
||||
#include "pwrseq.h"
|
||||
#include "sdio_ops.h"
|
||||
|
||||
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
|
||||
|
||||
|
@ -379,6 +380,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
spin_lock_init(&host->lock);
|
||||
init_waitqueue_head(&host->wq);
|
||||
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
|
||||
INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
|
||||
setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
|
||||
|
||||
/*
|
||||
|
|
|
@ -98,11 +98,27 @@ void sdio_run_irqs(struct mmc_host *host)
|
|||
if (host->sdio_irqs) {
|
||||
host->sdio_irq_pending = true;
|
||||
process_sdio_pending_irqs(host);
|
||||
if (host->ops->ack_sdio_irq)
|
||||
host->ops->ack_sdio_irq(host);
|
||||
}
|
||||
mmc_release_host(host);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdio_run_irqs);
|
||||
|
||||
void sdio_irq_work(struct work_struct *work)
|
||||
{
|
||||
struct mmc_host *host =
|
||||
container_of(work, struct mmc_host, sdio_irq_work.work);
|
||||
|
||||
sdio_run_irqs(host);
|
||||
}
|
||||
|
||||
void sdio_signal_irq(struct mmc_host *host)
|
||||
{
|
||||
queue_delayed_work(system_wq, &host->sdio_irq_work, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdio_signal_irq);
|
||||
|
||||
static int sdio_irq_thread(void *_host)
|
||||
{
|
||||
struct mmc_host *host = _host;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
struct mmc_host;
|
||||
struct mmc_card;
|
||||
struct work_struct;
|
||||
|
||||
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
|
||||
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
|
||||
|
@ -25,6 +26,7 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
|||
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
|
||||
int sdio_reset(struct mmc_host *host);
|
||||
unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz);
|
||||
void sdio_irq_work(struct work_struct *work);
|
||||
|
||||
static inline bool sdio_is_io_busy(u32 opcode, u32 arg)
|
||||
{
|
||||
|
|
|
@ -130,6 +130,7 @@ struct mmc_host_ops {
|
|||
int (*get_cd)(struct mmc_host *host);
|
||||
|
||||
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
|
||||
void (*ack_sdio_irq)(struct mmc_host *host);
|
||||
|
||||
/* optional callback for HC quirks */
|
||||
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
|
||||
|
@ -358,6 +359,7 @@ struct mmc_host {
|
|||
|
||||
unsigned int sdio_irqs;
|
||||
struct task_struct *sdio_irq_thread;
|
||||
struct delayed_work sdio_irq_work;
|
||||
bool sdio_irq_pending;
|
||||
atomic_t sdio_irq_thread_abort;
|
||||
|
||||
|
@ -428,6 +430,7 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
|||
}
|
||||
|
||||
void sdio_run_irqs(struct mmc_host *host);
|
||||
void sdio_signal_irq(struct mmc_host *host);
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
int mmc_regulator_get_ocrmask(struct regulator *supply);
|
||||
|
|
Loading…
Reference in New Issue