Message ID | 20231108141637.119497-1-yann.gautier@foss.st.com |
---|---|
State | New |
Headers | show |
Series | [v3] mmc: mmci: stm32: add SDIO in-band interrupt mode | expand |
On Wed, Nov 8, 2023 at 3:18 PM Yann Gautier <yann.gautier@foss.st.com> wrote: > From: Christophe Kerello <christophe.kerello@foss.st.com> > > 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> Still looks good to me! Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Yours, Linus Walleij
On Wed, 8 Nov 2023 at 15:18, Yann Gautier <yann.gautier@foss.st.com> wrote: > > From: Christophe Kerello <christophe.kerello@foss.st.com> > > 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> 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 >
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;