aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorStefan Nilsson XK <stefan.xk.nilsson@stericsson.com>2011-04-26 11:25:24 +0200
committerHenrik Aberg <henrik.aberg@stericsson.com>2011-05-18 09:40:08 +0200
commit44fd3adcd961b7fa5ceaa01d1631101a5c51e6cc (patch)
tree2c9ca31c2269eba08af89dd09c0cc0790dc952cf /drivers/mmc
parenta5681136f6b29e468253e53c30698410a0e57485 (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/mmc')
-rw-r--r--drivers/mmc/core/sdio_irq.c134
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, &param);
-
/*
- * 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;