···143143144144 drv->remove(func);145145146146+ if (func->irq_handler) {147147+ printk(KERN_WARNING "WARNING: driver %s did not remove "148148+ "its interrupt handler!\n", drv->name);149149+ sdio_claim_host(func);150150+ sdio_release_irq(func);151151+ sdio_release_host(func);152152+ }153153+146154 return 0;147155}148156
+237
drivers/mmc/core/sdio_irq.c
···11+/*22+ * linux/drivers/mmc/core/sdio_irq.c33+ *44+ * Author: Nicolas Pitre55+ * Created: June 18, 200766+ * Copyright: MontaVista Software Inc.77+ *88+ * This program is free software; you can redistribute it and/or modify99+ * it under the terms of the GNU General Public License as published by1010+ * the Free Software Foundation; either version 2 of the License, or (at1111+ * your option) any later version.1212+ */1313+1414+#include <linux/kernel.h>1515+#include <linux/sched.h>1616+#include <linux/kthread.h>1717+#include <linux/wait.h>1818+#include <linux/delay.h>1919+2020+#include <linux/mmc/core.h>2121+#include <linux/mmc/host.h>2222+#include <linux/mmc/card.h>2323+#include <linux/mmc/sdio.h>2424+#include <linux/mmc/sdio_func.h>2525+2626+#include "sdio_ops.h"2727+2828+static int process_sdio_pending_irqs(struct mmc_card *card)2929+{3030+ int i, ret;3131+ unsigned char pending;3232+3333+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);3434+ if (ret) {3535+ printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",3636+ mmc_card_id(card), ret);3737+ return ret;3838+ }3939+4040+ for (i = 1; i <= 7; i++) {4141+ if (pending & (1 << i)) {4242+ struct sdio_func *func = card->sdio_func[i - 1];4343+ if (!func) {4444+ printk(KERN_WARNING "%s: pending IRQ for "4545+ "non-existant function\n",4646+ sdio_func_id(func));4747+ } else if (func->irq_handler) {4848+ func->irq_handler(func);4949+ } else5050+ printk(KERN_WARNING "%s: pending IRQ with no handler\n",5151+ sdio_func_id(func));5252+ }5353+ }5454+5555+ return 0;5656+}5757+5858+static int sdio_irq_thread(void *_host)5959+{6060+ struct mmc_host *host = _host;6161+ struct sched_param param = { .sched_priority = 1 };6262+ unsigned long period;6363+ int ret;6464+6565+ sched_setscheduler(current, SCHED_FIFO, ¶m);6666+6767+ /*6868+ * We want to allow for SDIO cards to work even on non SDIO6969+ * aware hosts. One thing that non SDIO host cannot do is7070+ * asynchronous notification of pending SDIO card interrupts7171+ * hence we poll for them in that case.7272+ */7373+ period = msecs_to_jiffies(10);7474+7575+ pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",7676+ mmc_hostname(host), period);7777+7878+ do {7979+ /*8080+ * We claim the host here on drivers behalf for a couple8181+ * reasons:8282+ *8383+ * 1) it is already needed to retrieve the CCCR_INTx;8484+ * 2) we want the driver(s) to clear the IRQ condition ASAP;8585+ * 3) we need to control the abort condition locally.8686+ *8787+ * Just like traditional hard IRQ handlers, we expect SDIO8888+ * IRQ handlers to be quick and to the point, so that the8989+ * holding of the host lock does not cover too much work9090+ * that doesn't require that lock to be held.9191+ */9292+ ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);9393+ if (ret)9494+ break;9595+ ret = process_sdio_pending_irqs(host->card);9696+ mmc_release_host(host);9797+9898+ /*9999+ * Give other threads a chance to run in the presence of100100+ * errors. FIXME: determine if due to card removal and101101+ * possibly exit this thread if so.102102+ */103103+ if (ret)104104+ ssleep(1);105105+106106+ set_task_state(current, TASK_INTERRUPTIBLE);107107+ if (!kthread_should_stop())108108+ schedule_timeout(period);109109+ set_task_state(current, TASK_RUNNING);110110+ } while (!kthread_should_stop());111111+112112+ pr_debug("%s: IRQ thread exiting with code %d\n",113113+ mmc_hostname(host), ret);114114+115115+ return ret;116116+}117117+118118+static int sdio_card_irq_get(struct mmc_card *card)119119+{120120+ struct mmc_host *host = card->host;121121+122122+ BUG_ON(!host->claimed);123123+124124+ if (!host->sdio_irqs++) {125125+ atomic_set(&host->sdio_irq_thread_abort, 0);126126+ host->sdio_irq_thread =127127+ kthread_run(sdio_irq_thread, host, "ksdiorqd");128128+ if (IS_ERR(host->sdio_irq_thread)) {129129+ int err = PTR_ERR(host->sdio_irq_thread);130130+ host->sdio_irqs--;131131+ return err;132132+ }133133+ }134134+135135+ return 0;136136+}137137+138138+static int sdio_card_irq_put(struct mmc_card *card)139139+{140140+ struct mmc_host *host = card->host;141141+142142+ BUG_ON(!host->claimed);143143+ BUG_ON(host->sdio_irqs < 1);144144+145145+ if (!--host->sdio_irqs) {146146+ atomic_set(&host->sdio_irq_thread_abort, 1);147147+ kthread_stop(host->sdio_irq_thread);148148+ }149149+150150+ return 0;151151+}152152+153153+/**154154+ * sdio_claim_irq - claim the IRQ for a SDIO function155155+ * @func: SDIO function156156+ * @handler: IRQ handler callback157157+ *158158+ * Claim and activate the IRQ for the given SDIO function. The provided159159+ * handler will be called when that IRQ is asserted. The host is always160160+ * claimed already when the handler is called so the handler must not161161+ * call sdio_claim_host() nor sdio_release_host().162162+ */163163+int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)164164+{165165+ int ret;166166+ unsigned char reg;167167+168168+ BUG_ON(!func);169169+ BUG_ON(!func->card);170170+171171+ pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));172172+173173+ if (func->irq_handler) {174174+ pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));175175+ return -EBUSY;176176+ }177177+178178+ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);179179+ if (ret)180180+ return ret;181181+182182+ reg |= 1 << func->num;183183+184184+ reg |= 1; /* Master interrupt enable */185185+186186+ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);187187+ if (ret)188188+ return ret;189189+190190+ func->irq_handler = handler;191191+ ret = sdio_card_irq_get(func->card);192192+ if (ret)193193+ func->irq_handler = NULL;194194+195195+ return ret;196196+}197197+EXPORT_SYMBOL_GPL(sdio_claim_irq);198198+199199+/**200200+ * sdio_release_irq - release the IRQ for a SDIO function201201+ * @func: SDIO function202202+ *203203+ * Disable and release the IRQ for the given SDIO function.204204+ */205205+int sdio_release_irq(struct sdio_func *func)206206+{207207+ int ret;208208+ unsigned char reg;209209+210210+ BUG_ON(!func);211211+ BUG_ON(!func->card);212212+213213+ pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));214214+215215+ if (func->irq_handler) {216216+ func->irq_handler = NULL;217217+ sdio_card_irq_put(func->card);218218+ }219219+220220+ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);221221+ if (ret)222222+ return ret;223223+224224+ reg &= ~(1 << func->num);225225+226226+ /* Disable master interrupt with the last function interrupt */227227+ if (!(reg & 0xFE))228228+ reg = 0;229229+230230+ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);231231+ if (ret)232232+ return ret;233233+234234+ return 0;235235+}236236+EXPORT_SYMBOL_GPL(sdio_release_irq);237237+
+4
include/linux/mmc/host.h
···123123 unsigned int bus_refs; /* reference counter */124124 unsigned int bus_dead:1; /* bus has been released */125125126126+ unsigned int sdio_irqs;127127+ struct task_struct *sdio_irq_thread;128128+ atomic_t sdio_irq_thread_abort;129129+126130 unsigned long private[0] ____cacheline_aligned;127131};128132
+7
include/linux/mmc/sdio_func.h
···1616#include <linux/mod_devicetable.h>17171818struct mmc_card;1919+struct sdio_func;2020+2121+typedef void (sdio_irq_handler_t)(struct sdio_func *);19222023/*2124 * SDIO function CIS tuple (unknown to the core)···3633struct sdio_func {3734 struct mmc_card *card; /* the card this device belongs to */3835 struct device dev; /* the device */3636+ sdio_irq_handler_t *irq_handler; /* IRQ callback */3937 unsigned int num; /* function number */40384139 unsigned char class; /* standard interface class */···108104109105extern int sdio_enable_func(struct sdio_func *func);110106extern int sdio_disable_func(struct sdio_func *func);107107+108108+extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);109109+extern int sdio_release_irq(struct sdio_func *func);111110112111extern unsigned char sdio_readb(struct sdio_func *func,113112 unsigned int addr, int *err_ret);