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_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
  80static 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, &param);
  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                        mmc_host_clk_hold(host);
 151                        host->ops->enable_sdio_irq(host, 1);
 152                        mmc_host_clk_release(host);
 153                }
 154                if (!kthread_should_stop())
 155                        schedule_timeout(period);
 156                set_current_state(TASK_RUNNING);
 157        } while (!kthread_should_stop());
 158
 159        if (host->caps & MMC_CAP_SDIO_IRQ) {
 160                mmc_host_clk_hold(host);
 161                host->ops->enable_sdio_irq(host, 0);
 162                mmc_host_clk_release(host);
 163        }
 164
 165        pr_debug("%s: IRQ thread exiting with code %d\n",
 166                 mmc_hostname(host), ret);
 167
 168        return ret;
 169}
 170
 171static int sdio_card_irq_get(struct mmc_card *card)
 172{
 173        struct mmc_host *host = card->host;
 174
 175        WARN_ON(!host->claimed);
 176
 177        if (!host->sdio_irqs++) {
 178                atomic_set(&host->sdio_irq_thread_abort, 0);
 179                host->sdio_irq_thread =
 180                        kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
 181                                mmc_hostname(host));
 182                if (IS_ERR(host->sdio_irq_thread)) {
 183                        int err = PTR_ERR(host->sdio_irq_thread);
 184                        host->sdio_irqs--;
 185                        return err;
 186                }
 187        }
 188
 189        return 0;
 190}
 191
 192static int sdio_card_irq_put(struct mmc_card *card)
 193{
 194        struct mmc_host *host = card->host;
 195
 196        WARN_ON(!host->claimed);
 197        BUG_ON(host->sdio_irqs < 1);
 198
 199        if (!--host->sdio_irqs) {
 200                atomic_set(&host->sdio_irq_thread_abort, 1);
 201                kthread_stop(host->sdio_irq_thread);
 202        }
 203
 204        return 0;
 205}
 206
 207/* If there is only 1 function registered set sdio_single_irq */
 208static void sdio_single_irq_set(struct mmc_card *card)
 209{
 210        struct sdio_func *func;
 211        int i;
 212
 213        card->sdio_single_irq = NULL;
 214        if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&
 215            card->host->sdio_irqs == 1)
 216                for (i = 0; i < card->sdio_funcs; i++) {
 217                       func = card->sdio_func[i];
 218                       if (func && func->irq_handler) {
 219                               card->sdio_single_irq = func;
 220                               break;
 221                       }
 222               }
 223}
 224
 225/**
 226 *      sdio_claim_irq - claim the IRQ for a SDIO function
 227 *      @func: SDIO function
 228 *      @handler: IRQ handler callback
 229 *
 230 *      Claim and activate the IRQ for the given SDIO function. The provided
 231 *      handler will be called when that IRQ is asserted.  The host is always
 232 *      claimed already when the handler is called so the handler must not
 233 *      call sdio_claim_host() nor sdio_release_host().
 234 */
 235int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
 236{
 237        int ret;
 238        unsigned char reg;
 239
 240        BUG_ON(!func);
 241        BUG_ON(!func->card);
 242
 243        pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
 244
 245        if (func->irq_handler) {
 246                pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
 247                return -EBUSY;
 248        }
 249
 250        ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
 251        if (ret)
 252                return ret;
 253
 254        reg |= 1 << func->num;
 255
 256        reg |= 1; /* Master interrupt enable */
 257
 258        ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
 259        if (ret)
 260                return ret;
 261
 262        func->irq_handler = handler;
 263        ret = sdio_card_irq_get(func->card);
 264        if (ret)
 265                func->irq_handler = NULL;
 266        sdio_single_irq_set(func->card);
 267
 268        return ret;
 269}
 270EXPORT_SYMBOL_GPL(sdio_claim_irq);
 271
 272/**
 273 *      sdio_release_irq - release the IRQ for a SDIO function
 274 *      @func: SDIO function
 275 *
 276 *      Disable and release the IRQ for the given SDIO function.
 277 */
 278int sdio_release_irq(struct sdio_func *func)
 279{
 280        int ret;
 281        unsigned char reg;
 282
 283        BUG_ON(!func);
 284        BUG_ON(!func->card);
 285
 286        pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
 287
 288        if (func->irq_handler) {
 289                func->irq_handler = NULL;
 290                sdio_card_irq_put(func->card);
 291                sdio_single_irq_set(func->card);
 292        }
 293
 294        ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
 295        if (ret)
 296                return ret;
 297
 298        reg &= ~(1 << func->num);
 299
 300        /* Disable master interrupt with the last function interrupt */
 301        if (!(reg & 0xFE))
 302                reg = 0;
 303
 304        ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
 305        if (ret)
 306                return ret;
 307
 308        return 0;
 309}
 310EXPORT_SYMBOL_GPL(sdio_release_irq);
 311
 312