376 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * linux/drivers/mmc/core/sdio_irq.c4 *5 * Author: Nicolas Pitre6 * Created: June 18, 20077 * Copyright: MontaVista Software Inc.8 *9 * Copyright 2008 Pierre Ossman10 */11 12#include <linux/kernel.h>13#include <linux/sched.h>14#include <uapi/linux/sched/types.h>15#include <linux/kthread.h>16#include <linux/export.h>17#include <linux/wait.h>18#include <linux/delay.h>19 20#include <linux/mmc/core.h>21#include <linux/mmc/host.h>22#include <linux/mmc/card.h>23#include <linux/mmc/sdio.h>24#include <linux/mmc/sdio_func.h>25 26#include "sdio_ops.h"27#include "core.h"28#include "card.h"29 30static int sdio_get_pending_irqs(struct mmc_host *host, u8 *pending)31{32 struct mmc_card *card = host->card;33 int ret;34 35 WARN_ON(!host->claimed);36 37 ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, pending);38 if (ret) {39 pr_debug("%s: error %d reading SDIO_CCCR_INTx\n",40 mmc_card_id(card), ret);41 return ret;42 }43 44 if (*pending && mmc_card_broken_irq_polling(card) &&45 !(host->caps & MMC_CAP_SDIO_IRQ)) {46 unsigned char dummy;47 48 /* A fake interrupt could be created when we poll SDIO_CCCR_INTx49 * register with a Marvell SD8797 card. A dummy CMD52 read to50 * function 0 register 0xff can avoid this.51 */52 mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy);53 }54 55 return 0;56}57 58static int process_sdio_pending_irqs(struct mmc_host *host)59{60 struct mmc_card *card = host->card;61 int i, ret, count;62 bool sdio_irq_pending = host->sdio_irq_pending;63 unsigned char pending;64 struct sdio_func *func;65 66 /* Don't process SDIO IRQs if the card is suspended. */67 if (mmc_card_suspended(card))68 return 0;69 70 /* Clear the flag to indicate that we have processed the IRQ. */71 host->sdio_irq_pending = false;72 73 /*74 * Optimization, if there is only 1 function interrupt registered75 * and we know an IRQ was signaled then call irq handler directly.76 * Otherwise do the full probe.77 */78 func = card->sdio_single_irq;79 if (func && sdio_irq_pending) {80 func->irq_handler(func);81 return 1;82 }83 84 ret = sdio_get_pending_irqs(host, &pending);85 if (ret)86 return ret;87 88 count = 0;89 for (i = 1; i <= 7; i++) {90 if (pending & (1 << i)) {91 func = card->sdio_func[i - 1];92 if (!func) {93 pr_warn("%s: pending IRQ for non-existent function\n",94 mmc_card_id(card));95 ret = -EINVAL;96 } else if (func->irq_handler) {97 func->irq_handler(func);98 count++;99 } else {100 pr_warn("%s: pending IRQ with no handler\n",101 sdio_func_id(func));102 ret = -EINVAL;103 }104 }105 }106 107 if (count)108 return count;109 110 return ret;111}112 113static void sdio_run_irqs(struct mmc_host *host)114{115 mmc_claim_host(host);116 if (host->sdio_irqs) {117 process_sdio_pending_irqs(host);118 if (!host->sdio_irq_pending)119 host->ops->ack_sdio_irq(host);120 }121 mmc_release_host(host);122}123 124void sdio_irq_work(struct work_struct *work)125{126 struct mmc_host *host =127 container_of(work, struct mmc_host, sdio_irq_work);128 129 sdio_run_irqs(host);130}131 132void sdio_signal_irq(struct mmc_host *host)133{134 host->sdio_irq_pending = true;135 schedule_work(&host->sdio_irq_work);136}137EXPORT_SYMBOL_GPL(sdio_signal_irq);138 139static int sdio_irq_thread(void *_host)140{141 struct mmc_host *host = _host;142 unsigned long period, idle_period;143 int ret;144 145 sched_set_fifo_low(current);146 147 /*148 * We want to allow for SDIO cards to work even on non SDIO149 * aware hosts. One thing that non SDIO host cannot do is150 * asynchronous notification of pending SDIO card interrupts151 * hence we poll for them in that case.152 */153 idle_period = msecs_to_jiffies(10);154 period = (host->caps & MMC_CAP_SDIO_IRQ) ?155 MAX_SCHEDULE_TIMEOUT : idle_period;156 157 pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",158 mmc_hostname(host), period);159 160 do {161 /*162 * We claim the host here on drivers behalf for a couple163 * reasons:164 *165 * 1) it is already needed to retrieve the CCCR_INTx;166 * 2) we want the driver(s) to clear the IRQ condition ASAP;167 * 3) we need to control the abort condition locally.168 *169 * Just like traditional hard IRQ handlers, we expect SDIO170 * IRQ handlers to be quick and to the point, so that the171 * holding of the host lock does not cover too much work172 * that doesn't require that lock to be held.173 */174 ret = __mmc_claim_host(host, NULL,175 &host->sdio_irq_thread_abort);176 if (ret)177 break;178 ret = process_sdio_pending_irqs(host);179 mmc_release_host(host);180 181 /*182 * Give other threads a chance to run in the presence of183 * errors.184 */185 if (ret < 0) {186 set_current_state(TASK_INTERRUPTIBLE);187 if (!kthread_should_stop())188 schedule_timeout(HZ);189 set_current_state(TASK_RUNNING);190 }191 192 /*193 * Adaptive polling frequency based on the assumption194 * that an interrupt will be closely followed by more.195 * This has a substantial benefit for network devices.196 */197 if (!(host->caps & MMC_CAP_SDIO_IRQ)) {198 if (ret > 0)199 period /= 2;200 else {201 period++;202 if (period > idle_period)203 period = idle_period;204 }205 }206 207 set_current_state(TASK_INTERRUPTIBLE);208 if (host->caps & MMC_CAP_SDIO_IRQ)209 host->ops->enable_sdio_irq(host, 1);210 if (!kthread_should_stop())211 schedule_timeout(period);212 set_current_state(TASK_RUNNING);213 } while (!kthread_should_stop());214 215 if (host->caps & MMC_CAP_SDIO_IRQ)216 host->ops->enable_sdio_irq(host, 0);217 218 pr_debug("%s: IRQ thread exiting with code %d\n",219 mmc_hostname(host), ret);220 221 return ret;222}223 224static int sdio_card_irq_get(struct mmc_card *card)225{226 struct mmc_host *host = card->host;227 228 WARN_ON(!host->claimed);229 230 if (!host->sdio_irqs++) {231 if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {232 atomic_set(&host->sdio_irq_thread_abort, 0);233 host->sdio_irq_thread =234 kthread_run(sdio_irq_thread, host,235 "ksdioirqd/%s", mmc_hostname(host));236 if (IS_ERR(host->sdio_irq_thread)) {237 int err = PTR_ERR(host->sdio_irq_thread);238 host->sdio_irqs--;239 return err;240 }241 } else if (host->caps & MMC_CAP_SDIO_IRQ) {242 host->ops->enable_sdio_irq(host, 1);243 }244 }245 246 return 0;247}248 249static int sdio_card_irq_put(struct mmc_card *card)250{251 struct mmc_host *host = card->host;252 253 WARN_ON(!host->claimed);254 255 if (host->sdio_irqs < 1)256 return -EINVAL;257 258 if (!--host->sdio_irqs) {259 if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {260 atomic_set(&host->sdio_irq_thread_abort, 1);261 kthread_stop(host->sdio_irq_thread);262 } else if (host->caps & MMC_CAP_SDIO_IRQ) {263 host->ops->enable_sdio_irq(host, 0);264 }265 }266 267 return 0;268}269 270/* If there is only 1 function registered set sdio_single_irq */271static void sdio_single_irq_set(struct mmc_card *card)272{273 struct sdio_func *func;274 int i;275 276 card->sdio_single_irq = NULL;277 if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&278 card->host->sdio_irqs == 1) {279 for (i = 0; i < card->sdio_funcs; i++) {280 func = card->sdio_func[i];281 if (func && func->irq_handler) {282 card->sdio_single_irq = func;283 break;284 }285 }286 }287}288 289/**290 * sdio_claim_irq - claim the IRQ for a SDIO function291 * @func: SDIO function292 * @handler: IRQ handler callback293 *294 * Claim and activate the IRQ for the given SDIO function. The provided295 * handler will be called when that IRQ is asserted. The host is always296 * claimed already when the handler is called so the handler should not297 * call sdio_claim_host() or sdio_release_host().298 */299int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)300{301 int ret;302 unsigned char reg;303 304 if (!func)305 return -EINVAL;306 307 pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));308 309 if (func->irq_handler) {310 pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));311 return -EBUSY;312 }313 314 ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);315 if (ret)316 return ret;317 318 reg |= 1 << func->num;319 320 reg |= 1; /* Master interrupt enable */321 322 ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);323 if (ret)324 return ret;325 326 func->irq_handler = handler;327 ret = sdio_card_irq_get(func->card);328 if (ret)329 func->irq_handler = NULL;330 sdio_single_irq_set(func->card);331 332 return ret;333}334EXPORT_SYMBOL_GPL(sdio_claim_irq);335 336/**337 * sdio_release_irq - release the IRQ for a SDIO function338 * @func: SDIO function339 *340 * Disable and release the IRQ for the given SDIO function.341 */342int sdio_release_irq(struct sdio_func *func)343{344 int ret;345 unsigned char reg;346 347 if (!func)348 return -EINVAL;349 350 pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));351 352 if (func->irq_handler) {353 func->irq_handler = NULL;354 sdio_card_irq_put(func->card);355 sdio_single_irq_set(func->card);356 }357 358 ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);359 if (ret)360 return ret;361 362 reg &= ~(1 << func->num);363 364 /* Disable master interrupt with the last function interrupt */365 if (!(reg & 0xFE))366 reg = 0;367 368 ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);369 if (ret)370 return ret;371 372 return 0;373}374EXPORT_SYMBOL_GPL(sdio_release_irq);375 376