Commit 1bcfbfd7 authored by Christophe Kerello's avatar Christophe Kerello Committed by Ulf Hansson

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: default avatarChristophe Kerello <christophe.kerello@foss.st.com>
Signed-off-by: default avatarYann Gautier <yann.gautier@foss.st.com>
Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20231108141637.119497-1-yann.gautier@foss.st.comSigned-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 37c8ceb6
...@@ -273,6 +273,7 @@ static struct variant_data variant_stm32_sdmmc = { ...@@ -273,6 +273,7 @@ static struct variant_data variant_stm32_sdmmc = {
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.stm32_idmabsize_mask = GENMASK(12, 5), .stm32_idmabsize_mask = GENMASK(12, 5),
.stm32_idmabsize_align = BIT(5), .stm32_idmabsize_align = BIT(5),
.supports_sdio_irq = true,
.busy_timeout = true, .busy_timeout = true,
.busy_detect = true, .busy_detect = true,
.busy_detect_flag = MCI_STM32_BUSYD0, .busy_detect_flag = MCI_STM32_BUSYD0,
...@@ -300,6 +301,7 @@ static struct variant_data variant_stm32_sdmmcv2 = { ...@@ -300,6 +301,7 @@ static struct variant_data variant_stm32_sdmmcv2 = {
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.stm32_idmabsize_mask = GENMASK(16, 5), .stm32_idmabsize_mask = GENMASK(16, 5),
.stm32_idmabsize_align = BIT(5), .stm32_idmabsize_align = BIT(5),
.supports_sdio_irq = true,
.dma_lli = true, .dma_lli = true,
.busy_timeout = true, .busy_timeout = true,
.busy_detect = true, .busy_detect = true,
...@@ -328,6 +330,7 @@ static struct variant_data variant_stm32_sdmmcv3 = { ...@@ -328,6 +330,7 @@ static struct variant_data variant_stm32_sdmmcv3 = {
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.stm32_idmabsize_mask = GENMASK(16, 6), .stm32_idmabsize_mask = GENMASK(16, 6),
.stm32_idmabsize_align = BIT(6), .stm32_idmabsize_align = BIT(6),
.supports_sdio_irq = true,
.dma_lli = true, .dma_lli = true,
.busy_timeout = true, .busy_timeout = true,
.busy_detect = true, .busy_detect = true,
...@@ -421,8 +424,9 @@ void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) ...@@ -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) static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
{ {
/* Keep busy mode in DPSM if enabled */ /* Keep busy mode in DPSM and SDIO mask if enabled */
datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag; datactrl |= host->datactrl_reg & (host->variant->busy_dpsm_flag |
host->variant->datactrl_mask_sdio);
if (host->datactrl_reg != datactrl) { if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl; host->datactrl_reg = datactrl;
...@@ -1762,6 +1766,25 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) ...@@ -1762,6 +1766,25 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
return IRQ_HANDLED; 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. * Handle completion of command and data transfers.
*/ */
...@@ -1806,6 +1829,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) ...@@ -1806,6 +1829,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
mmci_data_irq(host, host->data, status); 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. * Busy detection has been handled by mmci_cmd_irq() above.
* Clear the status bit to prevent polling in IRQ context. * 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) ...@@ -2042,6 +2068,35 @@ static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
return ret; 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 = { static struct mmc_host_ops mmci_ops = {
.request = mmci_request, .request = mmci_request,
.pre_req = mmci_pre_request, .pre_req = mmci_pre_request,
...@@ -2317,6 +2372,16 @@ static int mmci_probe(struct amba_device *dev, ...@@ -2317,6 +2372,16 @@ static int mmci_probe(struct amba_device *dev,
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; 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. */ /* Variants with mandatory busy timeout in HW needs R1B responses. */
if (variant->busy_timeout) if (variant->busy_timeout)
mmc->caps |= MMC_CAP_NEED_RSP_BUSY; mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
......
...@@ -331,6 +331,7 @@ enum mmci_busy_state { ...@@ -331,6 +331,7 @@ enum mmci_busy_state {
* register. * register.
* @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
* @dma_lli: true if variant has dma link list feature. * @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. * @stm32_idmabsize_mask: stm32 sdmmc idma buffer size.
* @dma_flow_controller: use peripheral as flow controller for DMA. * @dma_flow_controller: use peripheral as flow controller for DMA.
*/ */
...@@ -377,6 +378,7 @@ struct variant_data { ...@@ -377,6 +378,7 @@ struct variant_data {
u32 start_err; u32 start_err;
u32 opendrain; u32 opendrain;
u8 dma_lli:1; u8 dma_lli:1;
bool supports_sdio_irq;
u32 stm32_idmabsize_mask; u32 stm32_idmabsize_mask;
u32 stm32_idmabsize_align; u32 stm32_idmabsize_align;
bool dma_flow_controller; bool dma_flow_controller;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment