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