2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2007-06-30 22:29:41 +08:00
|
|
|
/*
|
|
|
|
* linux/drivers/mmc/core/sdio_irq.c
|
|
|
|
*
|
|
|
|
* Author: Nicolas Pitre
|
|
|
|
* Created: June 18, 2007
|
|
|
|
* Copyright: MontaVista Software Inc.
|
|
|
|
*
|
2008-08-31 19:38:54 +08:00
|
|
|
* Copyright 2008 Pierre Ossman
|
2007-06-30 22:29:41 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/sched.h>
|
2017-02-02 01:07:51 +08:00
|
|
|
#include <uapi/linux/sched/types.h>
|
2007-06-30 22:29:41 +08:00
|
|
|
#include <linux/kthread.h>
|
2011-07-11 00:42:00 +08:00
|
|
|
#include <linux/export.h>
|
2007-06-30 22:29:41 +08:00
|
|
|
#include <linux/wait.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
|
|
|
|
#include <linux/mmc/core.h>
|
|
|
|
#include <linux/mmc/host.h>
|
|
|
|
#include <linux/mmc/card.h>
|
|
|
|
#include <linux/mmc/sdio.h>
|
|
|
|
#include <linux/mmc/sdio_func.h>
|
|
|
|
|
|
|
|
#include "sdio_ops.h"
|
2017-01-13 21:14:08 +08:00
|
|
|
#include "core.h"
|
2017-01-13 21:14:14 +08:00
|
|
|
#include "card.h"
|
2007-06-30 22:29:41 +08:00
|
|
|
|
2019-09-08 18:12:29 +08:00
|
|
|
static int sdio_get_pending_irqs(struct mmc_host *host, u8 *pending)
|
|
|
|
{
|
|
|
|
struct mmc_card *card = host->card;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
WARN_ON(!host->claimed);
|
|
|
|
|
|
|
|
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, pending);
|
|
|
|
if (ret) {
|
|
|
|
pr_debug("%s: error %d reading SDIO_CCCR_INTx\n",
|
|
|
|
mmc_card_id(card), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*pending && mmc_card_broken_irq_polling(card) &&
|
|
|
|
!(host->caps & MMC_CAP_SDIO_IRQ)) {
|
|
|
|
unsigned char dummy;
|
|
|
|
|
|
|
|
/* A fake interrupt could be created when we poll SDIO_CCCR_INTx
|
|
|
|
* register with a Marvell SD8797 card. A dummy CMD52 read to
|
|
|
|
* function 0 register 0xff can avoid this.
|
|
|
|
*/
|
|
|
|
mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-17 07:16:54 +08:00
|
|
|
static int process_sdio_pending_irqs(struct mmc_host *host)
|
2007-06-30 22:29:41 +08:00
|
|
|
{
|
2012-04-17 07:16:54 +08:00
|
|
|
struct mmc_card *card = host->card;
|
2007-09-27 16:48:29 +08:00
|
|
|
int i, ret, count;
|
mmc: core: Clarify sdio_irq_pending flag for MMC_CAP2_SDIO_IRQ_NOTHREAD
The sdio_irq_pending flag is used to let host drivers indicate that it has
signaled an IRQ. If that is the case and we only have a single SDIO func
that have claimed an SDIO IRQ, our assumption is that we can avoid reading
the SDIO_CCCR_INTx register and just call the SDIO func irq handler
immediately. This makes sense, but the flag is set/cleared in a somewhat
messy order, let's fix that up according to below.
First, the flag is currently set in sdio_run_irqs(), which is executed as a
work that was scheduled from sdio_signal_irq(). To make it more implicit
that the host have signaled an IRQ, let's instead immediately set the flag
in sdio_signal_irq(). This also makes the behavior consistent with host
drivers that uses the legacy, mmc_signal_sdio_irq() API. This have no
functional impact, because we don't expect host drivers to call
sdio_signal_irq() until after the work (sdio_run_irqs()) have been executed
anyways.
Second, currently we never clears the flag when using the sdio_run_irqs()
work, but only when using the sdio_irq_thread(). Let make the behavior
consistent, by moving the flag to be cleared inside the common
process_sdio_pending_irqs() function. Additionally, tweak the behavior of
the flag slightly, by avoiding to clear it unless we processed the SDIO
IRQ. The purpose with this at this point, is to keep the information about
whether there have been an SDIO IRQ signaled by the host, so at system
resume we can decide to process it without reading the SDIO_CCCR_INTx
register.
Tested-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
2019-09-08 18:12:30 +08:00
|
|
|
bool sdio_irq_pending = host->sdio_irq_pending;
|
2007-06-30 22:29:41 +08:00
|
|
|
unsigned char pending;
|
2011-05-11 23:48:05 +08:00
|
|
|
struct sdio_func *func;
|
|
|
|
|
2019-06-18 20:05:17 +08:00
|
|
|
/* Don't process SDIO IRQs if the card is suspended. */
|
|
|
|
if (mmc_card_suspended(card))
|
|
|
|
return 0;
|
|
|
|
|
mmc: core: Clarify sdio_irq_pending flag for MMC_CAP2_SDIO_IRQ_NOTHREAD
The sdio_irq_pending flag is used to let host drivers indicate that it has
signaled an IRQ. If that is the case and we only have a single SDIO func
that have claimed an SDIO IRQ, our assumption is that we can avoid reading
the SDIO_CCCR_INTx register and just call the SDIO func irq handler
immediately. This makes sense, but the flag is set/cleared in a somewhat
messy order, let's fix that up according to below.
First, the flag is currently set in sdio_run_irqs(), which is executed as a
work that was scheduled from sdio_signal_irq(). To make it more implicit
that the host have signaled an IRQ, let's instead immediately set the flag
in sdio_signal_irq(). This also makes the behavior consistent with host
drivers that uses the legacy, mmc_signal_sdio_irq() API. This have no
functional impact, because we don't expect host drivers to call
sdio_signal_irq() until after the work (sdio_run_irqs()) have been executed
anyways.
Second, currently we never clears the flag when using the sdio_run_irqs()
work, but only when using the sdio_irq_thread(). Let make the behavior
consistent, by moving the flag to be cleared inside the common
process_sdio_pending_irqs() function. Additionally, tweak the behavior of
the flag slightly, by avoiding to clear it unless we processed the SDIO
IRQ. The purpose with this at this point, is to keep the information about
whether there have been an SDIO IRQ signaled by the host, so at system
resume we can decide to process it without reading the SDIO_CCCR_INTx
register.
Tested-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
2019-09-08 18:12:30 +08:00
|
|
|
/* Clear the flag to indicate that we have processed the IRQ. */
|
|
|
|
host->sdio_irq_pending = false;
|
|
|
|
|
2011-05-11 23:48:05 +08:00
|
|
|
/*
|
|
|
|
* Optimization, if there is only 1 function interrupt registered
|
2012-04-17 07:16:54 +08:00
|
|
|
* and we know an IRQ was signaled then call irq handler directly.
|
|
|
|
* Otherwise do the full probe.
|
2011-05-11 23:48:05 +08:00
|
|
|
*/
|
|
|
|
func = card->sdio_single_irq;
|
mmc: core: Clarify sdio_irq_pending flag for MMC_CAP2_SDIO_IRQ_NOTHREAD
The sdio_irq_pending flag is used to let host drivers indicate that it has
signaled an IRQ. If that is the case and we only have a single SDIO func
that have claimed an SDIO IRQ, our assumption is that we can avoid reading
the SDIO_CCCR_INTx register and just call the SDIO func irq handler
immediately. This makes sense, but the flag is set/cleared in a somewhat
messy order, let's fix that up according to below.
First, the flag is currently set in sdio_run_irqs(), which is executed as a
work that was scheduled from sdio_signal_irq(). To make it more implicit
that the host have signaled an IRQ, let's instead immediately set the flag
in sdio_signal_irq(). This also makes the behavior consistent with host
drivers that uses the legacy, mmc_signal_sdio_irq() API. This have no
functional impact, because we don't expect host drivers to call
sdio_signal_irq() until after the work (sdio_run_irqs()) have been executed
anyways.
Second, currently we never clears the flag when using the sdio_run_irqs()
work, but only when using the sdio_irq_thread(). Let make the behavior
consistent, by moving the flag to be cleared inside the common
process_sdio_pending_irqs() function. Additionally, tweak the behavior of
the flag slightly, by avoiding to clear it unless we processed the SDIO
IRQ. The purpose with this at this point, is to keep the information about
whether there have been an SDIO IRQ signaled by the host, so at system
resume we can decide to process it without reading the SDIO_CCCR_INTx
register.
Tested-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
2019-09-08 18:12:30 +08:00
|
|
|
if (func && sdio_irq_pending) {
|
2011-05-11 23:48:05 +08:00
|
|
|
func->irq_handler(func);
|
|
|
|
return 1;
|
|
|
|
}
|
2007-06-30 22:29:41 +08:00
|
|
|
|
2019-09-08 18:12:29 +08:00
|
|
|
ret = sdio_get_pending_irqs(host, &pending);
|
|
|
|
if (ret)
|
2007-06-30 22:29:41 +08:00
|
|
|
return ret;
|
2013-11-27 07:39:20 +08:00
|
|
|
|
2007-09-27 16:48:29 +08:00
|
|
|
count = 0;
|
2007-06-30 22:29:41 +08:00
|
|
|
for (i = 1; i <= 7; i++) {
|
|
|
|
if (pending & (1 << i)) {
|
2011-05-11 23:48:05 +08:00
|
|
|
func = card->sdio_func[i - 1];
|
2007-06-30 22:29:41 +08:00
|
|
|
if (!func) {
|
2014-09-13 05:56:56 +08:00
|
|
|
pr_warn("%s: pending IRQ for non-existent function\n",
|
2007-10-04 03:32:10 +08:00
|
|
|
mmc_card_id(card));
|
2007-10-04 03:32:57 +08:00
|
|
|
ret = -EINVAL;
|
2007-06-30 22:29:41 +08:00
|
|
|
} else if (func->irq_handler) {
|
|
|
|
func->irq_handler(func);
|
2007-09-27 16:48:29 +08:00
|
|
|
count++;
|
2007-10-04 03:32:57 +08:00
|
|
|
} else {
|
2014-09-13 05:56:56 +08:00
|
|
|
pr_warn("%s: pending IRQ with no handler\n",
|
|
|
|
sdio_func_id(func));
|
2007-10-04 03:32:57 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
2007-06-30 22:29:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-04 03:32:57 +08:00
|
|
|
if (count)
|
|
|
|
return count;
|
|
|
|
|
|
|
|
return ret;
|
2007-06-30 22:29:41 +08:00
|
|
|
}
|
|
|
|
|
2019-06-18 23:34:42 +08:00
|
|
|
static void sdio_run_irqs(struct mmc_host *host)
|
2014-04-25 19:55:30 +08:00
|
|
|
{
|
|
|
|
mmc_claim_host(host);
|
2017-04-20 03:52:29 +08:00
|
|
|
if (host->sdio_irqs) {
|
|
|
|
process_sdio_pending_irqs(host);
|
mmc: core: Fixup processing of SDIO IRQs during system suspend/resume
System suspend/resume of SDIO cards, with SDIO IRQs enabled and when using
MMC_CAP2_SDIO_IRQ_NOTHREAD is unfortunate still suffering from a fragile
behaviour. Some problems have been taken care of so far, but more issues
remains.
For example, calling the ->ack_sdio_irq() callback to let host drivers
re-enable the SDIO IRQs is a bad idea, unless the IRQ have been consumed,
which may not be the case during system suspend/resume. This may lead to
that a host driver re-signals the same SDIO IRQ over and over again,
causing a storm of IRQs and gives a ping-pong effect towards the
sdio_irq_work().
Moreover, calling the ->enable_sdio_irq() callback at system resume to
re-enable already enabled SDIO IRQs for the host, causes the runtime PM
count for some host drivers to become in-balanced. This then leads to the
host to remain runtime resumed, no matter if it's needed or not.
To fix these problems, let's check if process_sdio_pending_irqs() actually
consumed the SDIO IRQ, before we continue to ack the IRQ by invoking the
->ack_sdio_irq() callback.
Additionally, there should be no need to re-enable SDIO IRQs as the host
driver already knows if they were enabled at system suspend, thus also
whether it needs to re-enable them at system resume. For this reason, drop
the call to ->enable_sdio_irq() during system resume.
In regards to these changes there is yet another issue, which is when there
is an SDIO IRQ being signaled by the host driver, but after the SDIO card
has been system suspended. Currently these IRQs are just thrown away, while
we should at least make sure to try to consume them when the SDIO card has
been system resumed. Fix this by queueing a sdio_irq_work() after we system
resumed the SDIO card.
Tested-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
2019-09-08 18:12:33 +08:00
|
|
|
if (!host->sdio_irq_pending)
|
|
|
|
host->ops->ack_sdio_irq(host);
|
2017-04-20 03:52:29 +08:00
|
|
|
}
|
2014-04-25 19:55:30 +08:00
|
|
|
mmc_release_host(host);
|
|
|
|
}
|
|
|
|
|
mmc: sdio: Add API to manage SDIO IRQs from a workqueue
For hosts not supporting MMC_CAP2_SDIO_IRQ_NOTHREAD but MMC_CAP_SDIO_IRQ,
the SDIO IRQs are processed from a dedicated kernel thread. For these
cases, the host calls mmc_signal_sdio_irq() from its ISR to signal a new
SDIO IRQ.
Signaling an SDIO IRQ makes the host's ->enable_sdio_irq() callback to be
invoked to temporary disable the IRQs, before the kernel thread is woken up
to process it. When processing of the IRQs are completed, they are
re-enabled by the kernel thread, again via invoking the host's
->enable_sdio_irq().
The observation from this, is that the execution path is being unnecessary
complex, as the host driver already knows that it needs to temporary
disable the IRQs before signaling a new one. Moreover, replacing the kernel
thread with a work/workqueue would not only greatly simplify the code, but
also make it more robust.
To address the above problems, let's continue to build upon the support for
MMC_CAP2_SDIO_IRQ_NOTHREAD, as it already implements SDIO IRQs to be
processed without using the clumsy kernel thread and without the ping-pong
calls of the host's ->enable_sdio_irq() callback for each processed IRQ.
Therefore, let's add new API sdio_signal_irq(), which enables hosts to
signal/process SDIO IRQs by using a work/workqueue, rather than using the
kernel thread.
Add also a new host callback ->ack_sdio_irq(), which the work invokes when
the SDIO IRQs have been processed. This informs the host about when it
shall re-enable the SDIO IRQs. Potentially, we could re-use the existing
->enable_sdio_irq() callback instead of adding a new one, however it has
turned out that it's more convenient for hosts to get this information via
a separate callback.
Hosts that wants to use this new method to signal/process SDIO IRQs, must
enable MMC_CAP2_SDIO_IRQ_NOTHREAD and implement the ->ack_sdio_irq()
callback.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
2017-04-13 22:48:11 +08:00
|
|
|
void sdio_irq_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct mmc_host *host =
|
|
|
|
container_of(work, struct mmc_host, sdio_irq_work.work);
|
|
|
|
|
|
|
|
sdio_run_irqs(host);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sdio_signal_irq(struct mmc_host *host)
|
|
|
|
{
|
mmc: core: Clarify sdio_irq_pending flag for MMC_CAP2_SDIO_IRQ_NOTHREAD
The sdio_irq_pending flag is used to let host drivers indicate that it has
signaled an IRQ. If that is the case and we only have a single SDIO func
that have claimed an SDIO IRQ, our assumption is that we can avoid reading
the SDIO_CCCR_INTx register and just call the SDIO func irq handler
immediately. This makes sense, but the flag is set/cleared in a somewhat
messy order, let's fix that up according to below.
First, the flag is currently set in sdio_run_irqs(), which is executed as a
work that was scheduled from sdio_signal_irq(). To make it more implicit
that the host have signaled an IRQ, let's instead immediately set the flag
in sdio_signal_irq(). This also makes the behavior consistent with host
drivers that uses the legacy, mmc_signal_sdio_irq() API. This have no
functional impact, because we don't expect host drivers to call
sdio_signal_irq() until after the work (sdio_run_irqs()) have been executed
anyways.
Second, currently we never clears the flag when using the sdio_run_irqs()
work, but only when using the sdio_irq_thread(). Let make the behavior
consistent, by moving the flag to be cleared inside the common
process_sdio_pending_irqs() function. Additionally, tweak the behavior of
the flag slightly, by avoiding to clear it unless we processed the SDIO
IRQ. The purpose with this at this point, is to keep the information about
whether there have been an SDIO IRQ signaled by the host, so at system
resume we can decide to process it without reading the SDIO_CCCR_INTx
register.
Tested-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
2019-09-08 18:12:30 +08:00
|
|
|
host->sdio_irq_pending = true;
|
mmc: sdio: Add API to manage SDIO IRQs from a workqueue
For hosts not supporting MMC_CAP2_SDIO_IRQ_NOTHREAD but MMC_CAP_SDIO_IRQ,
the SDIO IRQs are processed from a dedicated kernel thread. For these
cases, the host calls mmc_signal_sdio_irq() from its ISR to signal a new
SDIO IRQ.
Signaling an SDIO IRQ makes the host's ->enable_sdio_irq() callback to be
invoked to temporary disable the IRQs, before the kernel thread is woken up
to process it. When processing of the IRQs are completed, they are
re-enabled by the kernel thread, again via invoking the host's
->enable_sdio_irq().
The observation from this, is that the execution path is being unnecessary
complex, as the host driver already knows that it needs to temporary
disable the IRQs before signaling a new one. Moreover, replacing the kernel
thread with a work/workqueue would not only greatly simplify the code, but
also make it more robust.
To address the above problems, let's continue to build upon the support for
MMC_CAP2_SDIO_IRQ_NOTHREAD, as it already implements SDIO IRQs to be
processed without using the clumsy kernel thread and without the ping-pong
calls of the host's ->enable_sdio_irq() callback for each processed IRQ.
Therefore, let's add new API sdio_signal_irq(), which enables hosts to
signal/process SDIO IRQs by using a work/workqueue, rather than using the
kernel thread.
Add also a new host callback ->ack_sdio_irq(), which the work invokes when
the SDIO IRQs have been processed. This informs the host about when it
shall re-enable the SDIO IRQs. Potentially, we could re-use the existing
->enable_sdio_irq() callback instead of adding a new one, however it has
turned out that it's more convenient for hosts to get this information via
a separate callback.
Hosts that wants to use this new method to signal/process SDIO IRQs, must
enable MMC_CAP2_SDIO_IRQ_NOTHREAD and implement the ->ack_sdio_irq()
callback.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
2017-04-13 22:48:11 +08:00
|
|
|
queue_delayed_work(system_wq, &host->sdio_irq_work, 0);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(sdio_signal_irq);
|
|
|
|
|
2007-06-30 22:29:41 +08:00
|
|
|
static int sdio_irq_thread(void *_host)
|
|
|
|
{
|
|
|
|
struct mmc_host *host = _host;
|
2007-09-27 16:48:29 +08:00
|
|
|
unsigned long period, idle_period;
|
2007-06-30 22:29:41 +08:00
|
|
|
int ret;
|
|
|
|
|
2020-04-21 18:09:13 +08:00
|
|
|
sched_set_fifo_low(current);
|
2007-06-30 22:29:41 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We want to allow for SDIO cards to work even on non SDIO
|
|
|
|
* aware hosts. One thing that non SDIO host cannot do is
|
|
|
|
* asynchronous notification of pending SDIO card interrupts
|
|
|
|
* hence we poll for them in that case.
|
|
|
|
*/
|
2007-09-27 16:48:29 +08:00
|
|
|
idle_period = msecs_to_jiffies(10);
|
2007-07-24 14:09:39 +08:00
|
|
|
period = (host->caps & MMC_CAP_SDIO_IRQ) ?
|
2007-09-27 16:48:29 +08:00
|
|
|
MAX_SCHEDULE_TIMEOUT : idle_period;
|
2007-06-30 22:29:41 +08:00
|
|
|
|
|
|
|
pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
|
|
|
|
mmc_hostname(host), period);
|
|
|
|
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* We claim the host here on drivers behalf for a couple
|
|
|
|
* reasons:
|
|
|
|
*
|
|
|
|
* 1) it is already needed to retrieve the CCCR_INTx;
|
|
|
|
* 2) we want the driver(s) to clear the IRQ condition ASAP;
|
|
|
|
* 3) we need to control the abort condition locally.
|
|
|
|
*
|
|
|
|
* Just like traditional hard IRQ handlers, we expect SDIO
|
|
|
|
* IRQ handlers to be quick and to the point, so that the
|
|
|
|
* holding of the host lock does not cover too much work
|
|
|
|
* that doesn't require that lock to be held.
|
|
|
|
*/
|
2017-09-22 20:36:51 +08:00
|
|
|
ret = __mmc_claim_host(host, NULL,
|
|
|
|
&host->sdio_irq_thread_abort);
|
2007-06-30 22:29:41 +08:00
|
|
|
if (ret)
|
|
|
|
break;
|
2012-04-17 07:16:54 +08:00
|
|
|
ret = process_sdio_pending_irqs(host);
|
2007-06-30 22:29:41 +08:00
|
|
|
mmc_release_host(host);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Give other threads a chance to run in the presence of
|
2008-08-31 19:38:54 +08:00
|
|
|
* errors.
|
2007-06-30 22:29:41 +08:00
|
|
|
*/
|
2008-08-31 19:38:54 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
if (!kthread_should_stop())
|
|
|
|
schedule_timeout(HZ);
|
|
|
|
set_current_state(TASK_RUNNING);
|
|
|
|
}
|
2007-06-30 22:29:41 +08:00
|
|
|
|
2007-09-27 16:48:29 +08:00
|
|
|
/*
|
|
|
|
* Adaptive polling frequency based on the assumption
|
|
|
|
* that an interrupt will be closely followed by more.
|
|
|
|
* This has a substantial benefit for network devices.
|
|
|
|
*/
|
|
|
|
if (!(host->caps & MMC_CAP_SDIO_IRQ)) {
|
|
|
|
if (ret > 0)
|
|
|
|
period /= 2;
|
|
|
|
else {
|
|
|
|
period++;
|
|
|
|
if (period > idle_period)
|
|
|
|
period = idle_period;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-29 05:34:47 +08:00
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2015-10-02 16:56:11 +08:00
|
|
|
if (host->caps & MMC_CAP_SDIO_IRQ)
|
2007-07-24 14:09:39 +08:00
|
|
|
host->ops->enable_sdio_irq(host, 1);
|
2007-06-30 22:29:41 +08:00
|
|
|
if (!kthread_should_stop())
|
|
|
|
schedule_timeout(period);
|
2008-03-29 05:34:47 +08:00
|
|
|
set_current_state(TASK_RUNNING);
|
2007-06-30 22:29:41 +08:00
|
|
|
} while (!kthread_should_stop());
|
|
|
|
|
2015-10-02 16:56:11 +08:00
|
|
|
if (host->caps & MMC_CAP_SDIO_IRQ)
|
2007-07-24 14:09:39 +08:00
|
|
|
host->ops->enable_sdio_irq(host, 0);
|
|
|
|
|
2007-06-30 22:29:41 +08:00
|
|
|
pr_debug("%s: IRQ thread exiting with code %d\n",
|
|
|
|
mmc_hostname(host), ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdio_card_irq_get(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
struct mmc_host *host = card->host;
|
|
|
|
|
2007-08-09 19:23:56 +08:00
|
|
|
WARN_ON(!host->claimed);
|
2007-06-30 22:29:41 +08:00
|
|
|
|
|
|
|
if (!host->sdio_irqs++) {
|
2014-04-25 19:55:30 +08:00
|
|
|
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
|
|
|
|
atomic_set(&host->sdio_irq_thread_abort, 0);
|
|
|
|
host->sdio_irq_thread =
|
|
|
|
kthread_run(sdio_irq_thread, host,
|
|
|
|
"ksdioirqd/%s", mmc_hostname(host));
|
|
|
|
if (IS_ERR(host->sdio_irq_thread)) {
|
|
|
|
int err = PTR_ERR(host->sdio_irq_thread);
|
|
|
|
host->sdio_irqs--;
|
|
|
|
return err;
|
|
|
|
}
|
2014-08-18 10:48:14 +08:00
|
|
|
} else if (host->caps & MMC_CAP_SDIO_IRQ) {
|
2014-04-25 19:55:30 +08:00
|
|
|
host->ops->enable_sdio_irq(host, 1);
|
2007-06-30 22:29:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdio_card_irq_put(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
struct mmc_host *host = card->host;
|
|
|
|
|
2007-08-09 19:23:56 +08:00
|
|
|
WARN_ON(!host->claimed);
|
2016-11-02 15:24:00 +08:00
|
|
|
|
|
|
|
if (host->sdio_irqs < 1)
|
|
|
|
return -EINVAL;
|
2007-06-30 22:29:41 +08:00
|
|
|
|
|
|
|
if (!--host->sdio_irqs) {
|
2014-04-25 19:55:30 +08:00
|
|
|
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
|
|
|
|
atomic_set(&host->sdio_irq_thread_abort, 1);
|
|
|
|
kthread_stop(host->sdio_irq_thread);
|
2014-08-18 10:48:14 +08:00
|
|
|
} else if (host->caps & MMC_CAP_SDIO_IRQ) {
|
2014-04-25 19:55:30 +08:00
|
|
|
host->ops->enable_sdio_irq(host, 0);
|
|
|
|
}
|
2007-06-30 22:29:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-11 23:48:05 +08:00
|
|
|
/* If there is only 1 function registered set sdio_single_irq */
|
|
|
|
static void sdio_single_irq_set(struct mmc_card *card)
|
|
|
|
{
|
|
|
|
struct sdio_func *func;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
card->sdio_single_irq = NULL;
|
|
|
|
if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&
|
2020-02-22 00:31:47 +08:00
|
|
|
card->host->sdio_irqs == 1) {
|
2011-05-11 23:48:05 +08:00
|
|
|
for (i = 0; i < card->sdio_funcs; i++) {
|
2020-02-22 00:31:47 +08:00
|
|
|
func = card->sdio_func[i];
|
|
|
|
if (func && func->irq_handler) {
|
|
|
|
card->sdio_single_irq = func;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-05-11 23:48:05 +08:00
|
|
|
}
|
|
|
|
|
2007-06-30 22:29:41 +08:00
|
|
|
/**
|
|
|
|
* sdio_claim_irq - claim the IRQ for a SDIO function
|
|
|
|
* @func: SDIO function
|
|
|
|
* @handler: IRQ handler callback
|
|
|
|
*
|
|
|
|
* Claim and activate the IRQ for the given SDIO function. The provided
|
|
|
|
* handler will be called when that IRQ is asserted. The host is always
|
2018-03-19 22:15:38 +08:00
|
|
|
* claimed already when the handler is called so the handler should not
|
|
|
|
* call sdio_claim_host() or sdio_release_host().
|
2007-06-30 22:29:41 +08:00
|
|
|
*/
|
|
|
|
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
unsigned char reg;
|
|
|
|
|
2016-11-02 15:24:00 +08:00
|
|
|
if (!func)
|
|
|
|
return -EINVAL;
|
2007-06-30 22:29:41 +08:00
|
|
|
|
|
|
|
pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
|
|
|
|
|
|
|
|
if (func->irq_handler) {
|
|
|
|
pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
reg |= 1 << func->num;
|
|
|
|
|
|
|
|
reg |= 1; /* Master interrupt enable */
|
|
|
|
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
func->irq_handler = handler;
|
|
|
|
ret = sdio_card_irq_get(func->card);
|
|
|
|
if (ret)
|
|
|
|
func->irq_handler = NULL;
|
2011-05-11 23:48:05 +08:00
|
|
|
sdio_single_irq_set(func->card);
|
2007-06-30 22:29:41 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(sdio_claim_irq);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* sdio_release_irq - release the IRQ for a SDIO function
|
|
|
|
* @func: SDIO function
|
|
|
|
*
|
|
|
|
* Disable and release the IRQ for the given SDIO function.
|
|
|
|
*/
|
|
|
|
int sdio_release_irq(struct sdio_func *func)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
unsigned char reg;
|
|
|
|
|
2016-11-02 15:24:00 +08:00
|
|
|
if (!func)
|
|
|
|
return -EINVAL;
|
2007-06-30 22:29:41 +08:00
|
|
|
|
|
|
|
pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
|
|
|
|
|
|
|
|
if (func->irq_handler) {
|
|
|
|
func->irq_handler = NULL;
|
|
|
|
sdio_card_irq_put(func->card);
|
2011-05-11 23:48:05 +08:00
|
|
|
sdio_single_irq_set(func->card);
|
2007-06-30 22:29:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
reg &= ~(1 << func->num);
|
|
|
|
|
|
|
|
/* Disable master interrupt with the last function interrupt */
|
|
|
|
if (!(reg & 0xFE))
|
|
|
|
reg = 0;
|
|
|
|
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(sdio_release_irq);
|
|
|
|
|