1 /* 2 * linux/drivers/mmc/core/sdio_irq.c 3 * 4 * Author: Nicolas Pitre 5 * Created: June 18, 2007 6 * Copyright: MontaVista Software Inc. 7 * 8 * Copyright 2008 Pierre Ossman 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or (at 13 * your option) any later version. 14 */ 15 16 #include <linux/kernel.h> 17 #include <linux/sched.h> 18 #include <linux/kthread.h> 19 #include <linux/export.h> 20 #include <linux/wait.h> 21 #include <linux/delay.h> 22 23 #include <linux/mmc/core.h> 24 #include <linux/mmc/host.h> 25 #include <linux/mmc/card.h> 26 #include <linux/mmc/sdio.h> 27 #include <linux/mmc/sdio_func.h> 28 29 #include "sdio_ops.h" 30 31 static int process_sdio_pending_irqs(struct mmc_card *card) 32 { 33 int i, ret, count; 34 unsigned char pending; 35 struct sdio_func *func; 36 37 /* 38 * Optimization, if there is only 1 function interrupt registered 39 * call irq handler directly 40 */ 41 func = card->sdio_single_irq; 42 if (func) { 43 func->irq_handler(func); 44 return 1; 45 } 46 47 ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); 48 if (ret) { 49 pr_debug("%s: error %d reading SDIO_CCCR_INTx\n", 50 mmc_card_id(card), ret); 51 return ret; 52 } 53 54 count = 0; 55 for (i = 1; i <= 7; i++) { 56 if (pending & (1 << i)) { 57 func = card->sdio_func[i - 1]; 58 if (!func) { 59 pr_warning("%s: pending IRQ for " 60 "non-existent function\n", 61 mmc_card_id(card)); 62 ret = -EINVAL; 63 } else if (func->irq_handler) { 64 func->irq_handler(func); 65 count++; 66 } else { 67 pr_warning("%s: pending IRQ with no handler\n", 68 sdio_func_id(func)); 69 ret = -EINVAL; 70 } 71 } 72 } 73 74 if (count) 75 return count; 76 77 return ret; 78 } 79 80 static int sdio_irq_thread(void *_host) 81 { 82 struct mmc_host *host = _host; 83 struct sched_param param = { .sched_priority = 1 }; 84 unsigned long period, idle_period; 85 int ret; 86 87 sched_setscheduler(current, SCHED_FIFO, ¶m); 88 89 /* 90 * We want to allow for SDIO cards to work even on non SDIO 91 * aware hosts. One thing that non SDIO host cannot do is 92 * asynchronous notification of pending SDIO card interrupts 93 * hence we poll for them in that case. 94 */ 95 idle_period = msecs_to_jiffies(10); 96 period = (host->caps & MMC_CAP_SDIO_IRQ) ? 97 MAX_SCHEDULE_TIMEOUT : idle_period; 98 99 pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", 100 mmc_hostname(host), period); 101 102 do { 103 /* 104 * We claim the host here on drivers behalf for a couple 105 * reasons: 106 * 107 * 1) it is already needed to retrieve the CCCR_INTx; 108 * 2) we want the driver(s) to clear the IRQ condition ASAP; 109 * 3) we need to control the abort condition locally. 110 * 111 * Just like traditional hard IRQ handlers, we expect SDIO 112 * IRQ handlers to be quick and to the point, so that the 113 * holding of the host lock does not cover too much work 114 * that doesn't require that lock to be held. 115 */ 116 ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); 117 if (ret) 118 break; 119 ret = process_sdio_pending_irqs(host->card); 120 mmc_release_host(host); 121 122 /* 123 * Give other threads a chance to run in the presence of 124 * errors. 125 */ 126 if (ret < 0) { 127 set_current_state(TASK_INTERRUPTIBLE); 128 if (!kthread_should_stop()) 129 schedule_timeout(HZ); 130 set_current_state(TASK_RUNNING); 131 } 132 133 /* 134 * Adaptive polling frequency based on the assumption 135 * that an interrupt will be closely followed by more. 136 * This has a substantial benefit for network devices. 137 */ 138 if (!(host->caps & MMC_CAP_SDIO_IRQ)) { 139 if (ret > 0) 140 period /= 2; 141 else { 142 period++; 143 if (period > idle_period) 144 period = idle_period; 145 } 146 } 147 148 set_current_state(TASK_INTERRUPTIBLE); 149 if (host->caps & MMC_CAP_SDIO_IRQ) 150 host->ops->enable_sdio_irq(host, 1); 151 if (!kthread_should_stop()) 152 schedule_timeout(period); 153 set_current_state(TASK_RUNNING); 154 } while (!kthread_should_stop()); 155 156 if (host->caps & MMC_CAP_SDIO_IRQ) 157 host->ops->enable_sdio_irq(host, 0); 158 159 pr_debug("%s: IRQ thread exiting with code %d\n", 160 mmc_hostname(host), ret); 161 162 return ret; 163 } 164 165 static int sdio_card_irq_get(struct mmc_card *card) 166 { 167 struct mmc_host *host = card->host; 168 169 WARN_ON(!host->claimed); 170 171 if (!host->sdio_irqs++) { 172 atomic_set(&host->sdio_irq_thread_abort, 0); 173 host->sdio_irq_thread = 174 kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", 175 mmc_hostname(host)); 176 if (IS_ERR(host->sdio_irq_thread)) { 177 int err = PTR_ERR(host->sdio_irq_thread); 178 host->sdio_irqs--; 179 return err; 180 } 181 } 182 183 return 0; 184 } 185 186 static int sdio_card_irq_put(struct mmc_card *card) 187 { 188 struct mmc_host *host = card->host; 189 190 WARN_ON(!host->claimed); 191 BUG_ON(host->sdio_irqs < 1); 192 193 if (!--host->sdio_irqs) { 194 atomic_set(&host->sdio_irq_thread_abort, 1); 195 kthread_stop(host->sdio_irq_thread); 196 } 197 198 return 0; 199 } 200 201 /* If there is only 1 function registered set sdio_single_irq */ 202 static void sdio_single_irq_set(struct mmc_card *card) 203 { 204 struct sdio_func *func; 205 int i; 206 207 card->sdio_single_irq = NULL; 208 if ((card->host->caps & MMC_CAP_SDIO_IRQ) && 209 card->host->sdio_irqs == 1) 210 for (i = 0; i < card->sdio_funcs; i++) { 211 func = card->sdio_func[i]; 212 if (func && func->irq_handler) { 213 card->sdio_single_irq = func; 214 break; 215 } 216 } 217 } 218 219 /** 220 * sdio_claim_irq - claim the IRQ for a SDIO function 221 * @func: SDIO function 222 * @handler: IRQ handler callback 223 * 224 * Claim and activate the IRQ for the given SDIO function. The provided 225 * handler will be called when that IRQ is asserted. The host is always 226 * claimed already when the handler is called so the handler must not 227 * call sdio_claim_host() nor sdio_release_host(). 228 */ 229 int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) 230 { 231 int ret; 232 unsigned char reg; 233 234 BUG_ON(!func); 235 BUG_ON(!func->card); 236 237 pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func)); 238 239 if (func->irq_handler) { 240 pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func)); 241 return -EBUSY; 242 } 243 244 ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); 245 if (ret) 246 return ret; 247 248 reg |= 1 << func->num; 249 250 reg |= 1; /* Master interrupt enable */ 251 252 ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); 253 if (ret) 254 return ret; 255 256 func->irq_handler = handler; 257 ret = sdio_card_irq_get(func->card); 258 if (ret) 259 func->irq_handler = NULL; 260 sdio_single_irq_set(func->card); 261 262 return ret; 263 } 264 EXPORT_SYMBOL_GPL(sdio_claim_irq); 265 266 /** 267 * sdio_release_irq - release the IRQ for a SDIO function 268 * @func: SDIO function 269 * 270 * Disable and release the IRQ for the given SDIO function. 271 */ 272 int sdio_release_irq(struct sdio_func *func) 273 { 274 int ret; 275 unsigned char reg; 276 277 BUG_ON(!func); 278 BUG_ON(!func->card); 279 280 pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func)); 281 282 if (func->irq_handler) { 283 func->irq_handler = NULL; 284 sdio_card_irq_put(func->card); 285 sdio_single_irq_set(func->card); 286 } 287 288 ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); 289 if (ret) 290 return ret; 291 292 reg &= ~(1 << func->num); 293 294 /* Disable master interrupt with the last function interrupt */ 295 if (!(reg & 0xFE)) 296 reg = 0; 297 298 ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); 299 if (ret) 300 return ret; 301 302 return 0; 303 } 304 EXPORT_SYMBOL_GPL(sdio_release_irq); 305 306