This is an automatically generated commit which aids following Kernel patch history, as git will see the move and copy as a rename thus defeating the purpose. For the original discussion see: https://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html Signed-off-by: Thomas Richard <thomas.richard@bootlin.com> Link: https://github.com/openwrt/openwrt/pull/18740 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
		
			
				
	
	
		
			167 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 1bcfbfd7c9aa716f61a01682345a1b329f6a6e66 Mon Sep 17 00:00:00 2001
 | 
						|
From: Christophe Kerello <christophe.kerello@foss.st.com>
 | 
						|
Date: Wed, 8 Nov 2023 15:16:37 +0100
 | 
						|
Subject: [PATCH] mmc: mmci: stm32: add SDIO in-band interrupt mode
 | 
						|
 | 
						|
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 | 69 +++++++++++++++++++++++++++++++++++++++--
 | 
						|
 drivers/mmc/host/mmci.h |  2 ++
 | 
						|
 2 files changed, 69 insertions(+), 2 deletions(-)
 | 
						|
 | 
						|
--- a/drivers/mmc/host/mmci.c
 | 
						|
+++ b/drivers/mmc/host/mmci.c
 | 
						|
@@ -272,6 +272,7 @@ static struct variant_data variant_stm32
 | 
						|
 	.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,
 | 
						|
@@ -299,6 +300,7 @@ static struct variant_data variant_stm32
 | 
						|
 	.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,
 | 
						|
@@ -327,6 +329,7 @@ static struct variant_data variant_stm32
 | 
						|
 	.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,
 | 
						|
@@ -420,8 +423,9 @@ void mmci_write_pwrreg(struct mmci_host
 | 
						|
  */
 | 
						|
 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;
 | 
						|
@@ -1761,6 +1765,25 @@ static irqreturn_t mmci_pio_irq(int irq,
 | 
						|
 	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.
 | 
						|
  */
 | 
						|
@@ -1805,6 +1828,9 @@ static irqreturn_t mmci_irq(int irq, voi
 | 
						|
 			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.
 | 
						|
@@ -2041,6 +2067,35 @@ static int mmci_sig_volt_switch(struct m
 | 
						|
 	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,
 | 
						|
@@ -2316,6 +2371,16 @@ static int mmci_probe(struct amba_device
 | 
						|
 		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;
 | 
						|
--- 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.
 | 
						|
  */
 | 
						|
 struct variant_data {
 | 
						|
@@ -376,6 +377,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;
 | 
						|
 	void (*init)(struct mmci_host *host);
 |