]> err.no Git - linux-2.6/blobdiff - drivers/mmc/core/sdio_irq.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
[linux-2.6] / drivers / mmc / core / sdio_irq.c
index 01922d29241d73871a831a6bc3accca3a3b6d1d1..c292e124107ac13ec36be751024ad3728cafaa40 100644 (file)
@@ -27,7 +27,7 @@
 
 static int process_sdio_pending_irqs(struct mmc_card *card)
 {
-       int i, ret;
+       int i, ret, count;
        unsigned char pending;
 
        ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
@@ -37,29 +37,37 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
                return ret;
        }
 
+       count = 0;
        for (i = 1; i <= 7; i++) {
                if (pending & (1 << i)) {
                        struct sdio_func *func = card->sdio_func[i - 1];
                        if (!func) {
                                printk(KERN_WARNING "%s: pending IRQ for "
                                        "non-existant function\n",
-                                       sdio_func_id(func));
+                                       mmc_card_id(card));
+                               ret = -EINVAL;
                        } else if (func->irq_handler) {
                                func->irq_handler(func);
-                       } else
+                               count++;
+                       } else {
                                printk(KERN_WARNING "%s: pending IRQ with no handler\n",
                                       sdio_func_id(func));
+                               ret = -EINVAL;
+                       }
                }
        }
 
-       return 0;
+       if (count)
+               return count;
+
+       return ret;
 }
 
 static int sdio_irq_thread(void *_host)
 {
        struct mmc_host *host = _host;
        struct sched_param param = { .sched_priority = 1 };
-       unsigned long period;
+       unsigned long period, idle_period;
        int ret;
 
        sched_setscheduler(current, SCHED_FIFO, &param);
@@ -70,7 +78,9 @@ static int sdio_irq_thread(void *_host)
         * asynchronous notification of pending SDIO card interrupts
         * hence we poll for them in that case.
         */
-       period = msecs_to_jiffies(10);
+       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);
@@ -100,15 +110,35 @@ static int sdio_irq_thread(void *_host)
                 * errors.  FIXME: determine if due to card removal and
                 * possibly exit this thread if so.
                 */
-               if (ret)
+               if (ret < 0)
                        ssleep(1);
 
-               set_task_state(current, TASK_INTERRUPTIBLE);
+               /*
+                * 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;
+                       }
+               }
+
+               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_task_state(current, TASK_RUNNING);
+               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);
 
@@ -119,7 +149,7 @@ static int sdio_card_irq_get(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
 
-       BUG_ON(!host->claimed);
+       WARN_ON(!host->claimed);
 
        if (!host->sdio_irqs++) {
                atomic_set(&host->sdio_irq_thread_abort, 0);
@@ -139,7 +169,7 @@ static int sdio_card_irq_put(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
 
-       BUG_ON(!host->claimed);
+       WARN_ON(!host->claimed);
        BUG_ON(host->sdio_irqs < 1);
 
        if (!--host->sdio_irqs) {