diff options
author | Stefan Nilsson XK <stefan.xk.nilsson@stericsson.com> | 2011-04-26 11:25:24 +0200 |
---|---|---|
committer | Jonas ABERG <jonas.aberg@stericsson.com> | 2011-04-27 16:09:36 +0200 |
commit | 4479b94aba67ec4738d59c8fe004d31ad18e34f2 (patch) | |
tree | b27ee880ac3c7aa83048829619a3155faf5b1bb0 /drivers | |
parent | ffd9a25947a89a5c448426ff7498b55af296cf63 (diff) |
SDIO: Replace IRQ kthread with work queue
To make the IRQ handling of the SDIO framework more robust, the
kernel thread that used to handle SDIO IRQ:s is replaced with a
work queue. Both polling and regular IRQ mode is still supported.
ST-Ericsson ID: ER318044
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: Ibc1d10c00c972091d8880bd51962b5b45aa11ab3
Signed-off-by: Stefan Nilsson XK <stefan.xk.nilsson@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21665
Reviewed-by: QATEST
Reviewed-by: Sebastian RASMUSSEN <sebastian.rasmussen@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/core/sdio_irq.c | 134 |
1 files changed, 56 insertions, 78 deletions
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index bb192f90e8e..69802351996 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -27,6 +27,8 @@ #include "sdio_ops.h" +#define SDIO_IDLE_POLL_PERIOD 10 + static int process_sdio_pending_irqs(struct mmc_card *card) { int i, ret, count; @@ -65,89 +67,50 @@ static int process_sdio_pending_irqs(struct mmc_card *card) return ret; } -static int sdio_irq_thread(void *_host) +static void sdio_irq_work_func(struct work_struct *work) { - struct mmc_host *host = _host; - struct sched_param param = { .sched_priority = 1 }; - unsigned long period, idle_period; + struct mmc_host *host = (struct mmc_host *) + container_of(work, struct mmc_host, sdio_irq_work.work); int ret; - sched_setscheduler(current, SCHED_FIFO, ¶m); - /* - * We want to allow for SDIO cards to work even on non SDIO - * aware hosts. One thing that non SDIO host cannot do is - * asynchronous notification of pending SDIO card interrupts - * hence we poll for them in that case. + * We claim the host here on drivers behalf for a couple + * reasons: + * + * 1) it is already needed to retrieve the CCCR_INTx; + * 2) we want the driver(s) to clear the IRQ condition ASAP; + * 3) we need to control the abort condition locally. + * + * Just like traditional hard IRQ handlers, we expect SDIO + * IRQ handlers to be quick and to the point, so that the + * holding of the host lock does not cover too much work + * that doesn't require that lock to be held. */ - idle_period = msecs_to_jiffies(10); - period = (host->caps & MMC_CAP_SDIO_IRQ) ? - MAX_SCHEDULE_TIMEOUT : idle_period; - - pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", - mmc_hostname(host), period); + mmc_claim_host(host); - do { - /* - * We claim the host here on drivers behalf for a couple - * reasons: - * - * 1) it is already needed to retrieve the CCCR_INTx; - * 2) we want the driver(s) to clear the IRQ condition ASAP; - * 3) we need to control the abort condition locally. - * - * Just like traditional hard IRQ handlers, we expect SDIO - * IRQ handlers to be quick and to the point, so that the - * holding of the host lock does not cover too much work - * that doesn't require that lock to be held. - */ - ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); - if (ret) - break; - ret = process_sdio_pending_irqs(host->card); - mmc_release_host(host); + ret = process_sdio_pending_irqs(host->card); - /* - * Give other threads a chance to run in the presence of - * errors. - */ - if (ret < 0) { - set_current_state(TASK_INTERRUPTIBLE); - if (!kthread_should_stop()) - schedule_timeout(HZ); - set_current_state(TASK_RUNNING); - } + mmc_release_host(host); + if (host->caps & MMC_CAP_SDIO_IRQ) + host->ops->enable_sdio_irq(host, true); + else { /* * Adaptive polling frequency based on the assumption * that an interrupt will be closely followed by more. * This has a substantial benefit for network devices. */ - if (!(host->caps & MMC_CAP_SDIO_IRQ)) { - if (ret > 0) - period /= 2; - else { - period++; - if (period > idle_period) - period = idle_period; - } + if (ret > 0) + host->sdio_poll_period /= 2; + else { + host->sdio_poll_period++; + if (host->sdio_poll_period > SDIO_IDLE_POLL_PERIOD) + host->sdio_poll_period = SDIO_IDLE_POLL_PERIOD; } - - set_current_state(TASK_INTERRUPTIBLE); - if (host->caps & MMC_CAP_SDIO_IRQ) - host->ops->enable_sdio_irq(host, 1); - if (!kthread_should_stop()) - schedule_timeout(period); - set_current_state(TASK_RUNNING); - } while (!kthread_should_stop()); - - if (host->caps & MMC_CAP_SDIO_IRQ) - host->ops->enable_sdio_irq(host, 0); - - pr_debug("%s: IRQ thread exiting with code %d\n", - mmc_hostname(host), ret); - - return ret; + queue_delayed_work(host->sdio_irq_workqueue, + &host->sdio_irq_work, + msecs_to_jiffies(host->sdio_poll_period)); + } } static int sdio_card_irq_get(struct mmc_card *card) @@ -157,14 +120,28 @@ static int sdio_card_irq_get(struct mmc_card *card) WARN_ON(!host->claimed); if (!host->sdio_irqs++) { - atomic_set(&host->sdio_irq_thread_abort, 0); - host->sdio_irq_thread = - kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", - mmc_hostname(host)); - if (IS_ERR(host->sdio_irq_thread)) { - int err = PTR_ERR(host->sdio_irq_thread); + host->sdio_irq_workqueue = + create_singlethread_workqueue("sdio_irq_workqueue"); + if (host->sdio_irq_workqueue == NULL) { host->sdio_irqs--; - return err; + return -ENOMEM; + } + + INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work_func); + + /* + * We want to allow for SDIO cards to work even on non SDIO + * aware hosts. One thing that non SDIO host cannot do is + * asynchronous notification of pending SDIO card interrupts + * hence we poll for them in that case. + */ + if (host->caps & MMC_CAP_SDIO_IRQ) + host->ops->enable_sdio_irq(host, true); + else { + host->sdio_poll_period = SDIO_IDLE_POLL_PERIOD; + queue_delayed_work(host->sdio_irq_workqueue, + &host->sdio_irq_work, + msecs_to_jiffies(host->sdio_poll_period)); } } @@ -179,8 +156,9 @@ static int sdio_card_irq_put(struct mmc_card *card) BUG_ON(host->sdio_irqs < 1); if (!--host->sdio_irqs) { - atomic_set(&host->sdio_irq_thread_abort, 1); - kthread_stop(host->sdio_irq_thread); + host->ops->enable_sdio_irq(host, false); + destroy_workqueue(host->sdio_irq_workqueue); + host->sdio_irq_workqueue = NULL; } return 0; |