On Wed, 8 Nov 2023 at 15:18, Yann Gautier <[email protected]> wrote:
>
> From: Christophe Kerello <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Yann Gautier <[email protected]>
Applied for next, thanks!
Kind regards
Uffe
> ---
> Updates on v3:
> * remove variant ops enable_sdio_irq & sdio_irq and use common
> functions (mmci_write_sdio_irq_bit() & mmci_signal_sdio_irq())
> * enable datactrl_mask_sdio during probe if relevant and keep it
> enabled in mmci_write_datactrlreg().
>
> Updates on v2:
> * rename use_sdio_irq to supports_sdio_irq and change it to bool
> * use common code for ux500 and stm32 variants
>
> ---
> drivers/mmc/host/mmci.c | 69 +++++++++++++++++++++++++++++++++++++++--
> drivers/mmc/host/mmci.h | 2 ++
> 2 files changed, 69 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index e967cca7a16f..b790c3c3c8f9 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -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;
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index 34d9897c289b..a5eb4ced4d5d 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -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;
> --
> 2.34.1
>