LCOV - code coverage report
Current view: top level - drivers/mmc/core - sdio_irq.c (source / functions) Hit Total Coverage
Test: Real Lines: 0 135 0.0 %
Date: 2020-10-17 15:46:43 Functions: 0 11 0.0 %
Legend: Neither, QEMU, Real, Both Branches: 0 0 -

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

Generated by: LCOV version 1.14