Finally, the goal of all these changes, add a hard SDIO interrupt handler which will be called directly from interrupt context. Nothing really tricky here, the patch adds a new function sdio_claim_hard_irq and moves the common code from sdio_hard_irq into __sdio_claim_irq. It also adds some code that calls the hard interrupt handler directly from interrupt context. One very important thing here is that since the hard interrupt handler will return immediately, it has to call sdio_release_host when it is done. The call to sdio_kick_irq do the right thing, either process the next hard interrupt, wake up the soft interrupt workqueue or return 0 which really releases the host. This work is done for my employer, CSR. Signed-off-by: Christer Weinigel CSR Index: linux-2.6.26.2/include/linux/mmc/sdio_func.h =================================================================== --- linux-2.6.26.2.orig/include/linux/mmc/sdio_func.h +++ linux-2.6.26.2/include/linux/mmc/sdio_func.h @@ -15,6 +15,8 @@ #include #include +#define SDIO_HAVE_HARD_IRQ 1 + struct mmc_card; struct sdio_func; @@ -36,7 +38,8 @@ struct sdio_func_tuple { struct sdio_func { struct mmc_card *card; /* the card this device belongs to */ struct device dev; /* the device */ - sdio_irq_handler_t *irq_handler; /* IRQ callback */ + sdio_irq_handler_t *irq_handler; /* IRQ thread callback */ + sdio_irq_handler_t *hard_irq_handler; /* hard IRQ callback */ unsigned int num; /* function number */ unsigned char class; /* standard interface class */ @@ -110,6 +113,8 @@ extern void sdio_unregister_driver(struc * SDIO I/O operations */ extern void sdio_claim_host(struct sdio_func *func); +extern int sdio_claim_hard_irq(struct sdio_func *func, + sdio_irq_handler_t *handler); extern void sdio_release_host(struct sdio_func *func); extern int sdio_enable_func(struct sdio_func *func); Index: linux-2.6.26.2/drivers/mmc/core/sdio_irq.c =================================================================== --- linux-2.6.26.2.orig/drivers/mmc/core/sdio_irq.c +++ linux-2.6.26.2/drivers/mmc/core/sdio_irq.c @@ -23,8 +23,27 @@ #include #include +#include "core.h" #include "sdio_ops.h" +static int sdio_process_hard_irqs(struct mmc_card *card) +{ + int i; + + for (i = 1; i <= 7; i++) { + if (card->pending_irqs & (1 << i)) { + struct sdio_func *func = card->sdio_func[i - 1]; + if (func && func->hard_irq_handler) { + card->pending_irqs &= ~(1 << i); + func->hard_irq_handler(func); + return 1; + } + } + } + + return 0; +} + static int process_sdio_pending_irqs(struct mmc_card *card) { int i, ret, count; @@ -150,7 +169,8 @@ int sdio_kick_irq(struct mmc_host *host) return 0; if (host->sdio_irqs && host->card->pending_irqs) { - queue_work(host->irq_workqueue, &host->irq_work); + if (!sdio_process_hard_irqs(host->card)) + queue_work(host->irq_workqueue, &host->irq_work); return 1; } @@ -181,6 +201,12 @@ void mmc_signal_sdio_irq(struct mmc_host return; } + if (!host->card) { + pr_debug("%s: card is removed\n", __func__); + mmc_release_host(host); + return; + } + mmc_io_rw_direct_start(host->card, 0, 0, SDIO_CCCR_INTx, 0, 1, sdio_irq_pending_done, host); } @@ -223,17 +249,8 @@ static void sdio_irq_pending_done(struct mmc_release_host(host); } -/** - * sdio_claim_irq - claim the IRQ for a SDIO function - * @func: SDIO function - * @handler: IRQ handler callback - * - * Claim and activate the IRQ for the given SDIO function. The provided - * handler will be called when that IRQ is asserted. The host is always - * claimed already when the handler is called so the handler must not - * call sdio_claim_host() nor sdio_release_host(). - */ -int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) +static int __sdio_claim_irq(struct sdio_func *func, + sdio_irq_handler_t *handler, bool hard) { int ret; unsigned char reg; @@ -243,7 +260,7 @@ int sdio_claim_irq(struct sdio_func *fun pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func)); - if (func->irq_handler) { + if (func->irq_handler || func->hard_irq_handler) { pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func)); return -EBUSY; } @@ -260,16 +277,54 @@ int sdio_claim_irq(struct sdio_func *fun if (ret) return ret; - func->irq_handler = handler; + if (hard) + func->hard_irq_handler = handler; + else + func->irq_handler = handler; ret = sdio_card_irq_get(func->card); - if (ret) + if (ret) { func->irq_handler = NULL; + func->hard_irq_handler = NULL; + } return ret; } + +/** + * sdio_claim_irq - claim the IRQ for a SDIO function + * @func: SDIO function + * @handler: IRQ handler callback + * + * Claim and activate the IRQ for the given SDIO function. The provided + * handler will be called when that IRQ is asserted. The host is always + * claimed already when the handler is called so the handler must not + * call sdio_claim_host() nor sdio_release_host(). + */ +int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) +{ + return __sdio_claim_irq(func, handler, 0); +} EXPORT_SYMBOL_GPL(sdio_claim_irq); /** + * sdio_claim_hard_irq - claim the IRQ for a SDIO function + * @func: SDIO function + * @handler: IRQ handler callback + * + * Claim and activate the IRQ for the given SDIO function. The + * provided handler will be called directly from the interrupt + * handler when that IRQ is asserted, so the function can not + * sleep. The host is always claimed already when the handler is + * called so the handler must not call sdio_claim_host(). When + * handler is done the function should call sdio_release_host(). + */ +int sdio_claim_hard_irq(struct sdio_func *func, sdio_irq_handler_t *handler) +{ + return __sdio_claim_irq(func, handler, 1); +} +EXPORT_SYMBOL_GPL(sdio_claim_hard_irq); + +/** * sdio_release_irq - release the IRQ for a SDIO function * @func: SDIO function * @@ -287,6 +342,7 @@ int sdio_release_irq(struct sdio_func *f if (func->irq_handler) { func->irq_handler = NULL; + func->hard_irq_handler = NULL; sdio_card_irq_put(func->card); } -- "Just how much can I get away with and still go to heaven?" Christer Weinigel http://www.weinigel.se -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/