]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mmc: mmci: stm32: add SDIO in-band interrupt mode
authorChristophe Kerello <christophe.kerello@foss.st.com>
Wed, 8 Nov 2023 14:16:37 +0000 (15:16 +0100)
committerUlf Hansson <ulf.hansson@linaro.org>
Thu, 7 Dec 2023 13:08:44 +0000 (14:08 +0100)
Add the support of SDIO in-band interrupt mode for STM32 and Ux500
variants.
It allows the SD I/O card to interrupt the host on SDMMC_D1 data line.
It is not enabled by default on Ux500 variant as this is unstable and
Ux500 users should use out-of-band IRQs.

Signed-off-by: Christophe Kerello <christophe.kerello@foss.st.com>
Signed-off-by: Yann Gautier <yann.gautier@foss.st.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20231108141637.119497-1-yann.gautier@foss.st.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/mmci.c
drivers/mmc/host/mmci.h

index e967cca7a16f908a6f70710e391f1035fcf16e49..b790c3c3c8f99772010c13497969dfb7edb8020c 100644 (file)
@@ -273,6 +273,7 @@ static struct variant_data variant_stm32_sdmmc = {
        .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
        .stm32_idmabsize_mask   = GENMASK(12, 5),
        .stm32_idmabsize_align  = BIT(5),
+       .supports_sdio_irq      = true,
        .busy_timeout           = true,
        .busy_detect            = true,
        .busy_detect_flag       = MCI_STM32_BUSYD0,
@@ -300,6 +301,7 @@ static struct variant_data variant_stm32_sdmmcv2 = {
        .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
        .stm32_idmabsize_mask   = GENMASK(16, 5),
        .stm32_idmabsize_align  = BIT(5),
+       .supports_sdio_irq      = true,
        .dma_lli                = true,
        .busy_timeout           = true,
        .busy_detect            = true,
@@ -328,6 +330,7 @@ static struct variant_data variant_stm32_sdmmcv3 = {
        .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
        .stm32_idmabsize_mask   = GENMASK(16, 6),
        .stm32_idmabsize_align  = BIT(6),
+       .supports_sdio_irq      = true,
        .dma_lli                = true,
        .busy_timeout           = true,
        .busy_detect            = true,
@@ -421,8 +424,9 @@ void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
  */
 static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
 {
-       /* Keep busy mode in DPSM if enabled */
-       datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
+       /* Keep busy mode in DPSM and SDIO mask if enabled */
+       datactrl |= host->datactrl_reg & (host->variant->busy_dpsm_flag |
+                                         host->variant->datactrl_mask_sdio);
 
        if (host->datactrl_reg != datactrl) {
                host->datactrl_reg = datactrl;
@@ -1762,6 +1766,25 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void mmci_write_sdio_irq_bit(struct mmci_host *host, int enable)
+{
+       void __iomem *base = host->base;
+       u32 mask = readl_relaxed(base + MMCIMASK0);
+
+       if (enable)
+               writel_relaxed(mask | MCI_ST_SDIOITMASK, base + MMCIMASK0);
+       else
+               writel_relaxed(mask & ~MCI_ST_SDIOITMASK, base + MMCIMASK0);
+}
+
+static void mmci_signal_sdio_irq(struct mmci_host *host, u32 status)
+{
+       if (status & MCI_ST_SDIOIT) {
+               mmci_write_sdio_irq_bit(host, 0);
+               sdio_signal_irq(host->mmc);
+       }
+}
+
 /*
  * Handle completion of command and data transfers.
  */
@@ -1806,6 +1829,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
                        mmci_data_irq(host, host->data, status);
                }
 
+               if (host->variant->supports_sdio_irq)
+                       mmci_signal_sdio_irq(host, status);
+
                /*
                 * Busy detection has been handled by mmci_cmd_irq() above.
                 * Clear the status bit to prevent polling in IRQ context.
@@ -2042,6 +2068,35 @@ static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
        return ret;
 }
 
+static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+       struct mmci_host *host = mmc_priv(mmc);
+       unsigned long flags;
+
+       if (enable)
+               /* Keep the SDIO mode bit if SDIO irqs are enabled */
+               pm_runtime_get_sync(mmc_dev(mmc));
+
+       spin_lock_irqsave(&host->lock, flags);
+       mmci_write_sdio_irq_bit(host, enable);
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       if (!enable) {
+               pm_runtime_mark_last_busy(mmc_dev(mmc));
+               pm_runtime_put_autosuspend(mmc_dev(mmc));
+       }
+}
+
+static void mmci_ack_sdio_irq(struct mmc_host *mmc)
+{
+       struct mmci_host *host = mmc_priv(mmc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->lock, flags);
+       mmci_write_sdio_irq_bit(host, 1);
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
 static struct mmc_host_ops mmci_ops = {
        .request        = mmci_request,
        .pre_req        = mmci_pre_request,
@@ -2317,6 +2372,16 @@ static int mmci_probe(struct amba_device *dev,
                mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
        }
 
+       if (variant->supports_sdio_irq && host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+               mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
+               mmci_ops.enable_sdio_irq = mmci_enable_sdio_irq;
+               mmci_ops.ack_sdio_irq   = mmci_ack_sdio_irq;
+
+               mmci_write_datactrlreg(host,
+                                      host->variant->datactrl_mask_sdio);
+       }
+
        /* Variants with mandatory busy timeout in HW needs R1B responses. */
        if (variant->busy_timeout)
                mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
index 34d9897c289b3fc7dd4c88412ba9e00767564737..a5eb4ced4d5d2efa0800772b6be7f3eba2c7d107 100644 (file)
@@ -331,6 +331,7 @@ enum mmci_busy_state {
  *            register.
  * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
  * @dma_lli: true if variant has dma link list feature.
+ * @supports_sdio_irq: allow SD I/O card to interrupt the host
  * @stm32_idmabsize_mask: stm32 sdmmc idma buffer size.
  * @dma_flow_controller: use peripheral as flow controller for DMA.
  */
@@ -377,6 +378,7 @@ struct variant_data {
        u32                     start_err;
        u32                     opendrain;
        u8                      dma_lli:1;
+       bool                    supports_sdio_irq;
        u32                     stm32_idmabsize_mask;
        u32                     stm32_idmabsize_align;
        bool                    dma_flow_controller;