aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci.c
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2017-03-20 19:50:47 +0200
committerUlf Hansson <ulf.hansson@linaro.org>2017-04-24 21:41:36 +0200
commitf12e39dbf3e6eb521435085712addc131d803bfd (patch)
tree076051b511437820d18682f4079c23a721ed9876 /drivers/mmc/host/sdhci.c
parentmmc: sdhci: Factor out sdhci_set_default_irqs (diff)
downloadlinux-dev-f12e39dbf3e6eb521435085712addc131d803bfd.tar.xz
linux-dev-f12e39dbf3e6eb521435085712addc131d803bfd.zip
mmc: sdhci: Add CQE support
Add an interrupt hook and helper functions for enabling, disabling and delivering interrupts to a CQE. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Tested-by: Ludovic Desroches <ludovic.desroches@microchip.com>
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r--drivers/mmc/host/sdhci.c130
1 files changed, 127 insertions, 3 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 176e07f7c6ea..a971fcfa6de7 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -252,6 +252,8 @@ static void sdhci_init(struct sdhci_host *host, int soft)
sdhci_set_default_irqs(host);
+ host->cqe_on = false;
+
if (soft) {
/* force clock reconfiguration */
host->clock = 0;
@@ -2672,13 +2674,19 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
}
do {
+ DBG("IRQ status 0x%08x\n", intmask);
+
+ if (host->ops->irq) {
+ intmask = host->ops->irq(host, intmask);
+ if (!intmask)
+ goto cont;
+ }
+
/* Clear selected interrupts. */
mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
SDHCI_INT_BUS_POWER);
sdhci_writel(host, mask, SDHCI_INT_STATUS);
- DBG("IRQ status 0x%08x\n", intmask);
-
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT;
@@ -2738,7 +2746,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
unexpected |= intmask;
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
}
-
+cont:
if (result == IRQ_NONE)
result = IRQ_HANDLED;
@@ -2967,6 +2975,119 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
/*****************************************************************************\
* *
+ * Command Queue Engine (CQE) helpers *
+ * *
+\*****************************************************************************/
+
+void sdhci_cqe_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ u8 ctrl;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl &= ~SDHCI_CTRL_DMA_MASK;
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ ctrl |= SDHCI_CTRL_ADMA64;
+ else
+ ctrl |= SDHCI_CTRL_ADMA32;
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 512),
+ SDHCI_BLOCK_SIZE);
+
+ /* Set maximum timeout */
+ sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL);
+
+ host->ier = host->cqe_ier;
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ host->cqe_on = true;
+
+ pr_debug("%s: sdhci: CQE on, IRQ mask %#x, IRQ status %#x\n",
+ mmc_hostname(mmc), host->ier,
+ sdhci_readl(host, SDHCI_INT_STATUS));
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sdhci_cqe_enable);
+
+void sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ sdhci_set_default_irqs(host);
+
+ host->cqe_on = false;
+
+ if (recovery) {
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
+ }
+
+ pr_debug("%s: sdhci: CQE off, IRQ mask %#x, IRQ status %#x\n",
+ mmc_hostname(mmc), host->ier,
+ sdhci_readl(host, SDHCI_INT_STATUS));
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sdhci_cqe_disable);
+
+bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
+ int *data_error)
+{
+ u32 mask;
+
+ if (!host->cqe_on)
+ return false;
+
+ if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC))
+ *cmd_error = -EILSEQ;
+ else if (intmask & SDHCI_INT_TIMEOUT)
+ *cmd_error = -ETIMEDOUT;
+ else
+ *cmd_error = 0;
+
+ if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC))
+ *data_error = -EILSEQ;
+ else if (intmask & SDHCI_INT_DATA_TIMEOUT)
+ *data_error = -ETIMEDOUT;
+ else if (intmask & SDHCI_INT_ADMA_ERROR)
+ *data_error = -EIO;
+ else
+ *data_error = 0;
+
+ /* Clear selected interrupts. */
+ mask = intmask & host->cqe_ier;
+ sdhci_writel(host, mask, SDHCI_INT_STATUS);
+
+ if (intmask & SDHCI_INT_BUS_POWER)
+ pr_err("%s: Card is consuming too much power!\n",
+ mmc_hostname(host->mmc));
+
+ intmask &= ~(host->cqe_ier | SDHCI_INT_ERROR);
+ if (intmask) {
+ sdhci_writel(host, intmask, SDHCI_INT_STATUS);
+ pr_err("%s: CQE: Unexpected interrupt 0x%08x.\n",
+ mmc_hostname(host->mmc), intmask);
+ sdhci_dumpregs(host);
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(sdhci_cqe_irq);
+
+/*****************************************************************************\
+ * *
* Device allocation/registration *
* *
\*****************************************************************************/
@@ -2990,6 +3111,9 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->flags = SDHCI_SIGNALING_330;
+ host->cqe_ier = SDHCI_CQE_INT_MASK;
+ host->cqe_err_ier = SDHCI_CQE_INT_ERR_MASK;
+
return host;
}