X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fmmc%2Fcore%2Fsdio_irq.c;h=c292e124107ac13ec36be751024ad3728cafaa40;hb=d65f5c5803d9cd6fa0b540a0dddf956be671bc36;hp=01922d29241d73871a831a6bc3accca3a3b6d1d1;hpb=d1496c39e500857b8949cdb91af24e0eb8aae4d0;p=linux-2.6 diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 01922d2924..c292e12410 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -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, ¶m); @@ -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) {