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