]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
dmaengine: qcom: gpi: Add GPI Block event interrupt support
authorJyothi Kumar Seerapu <quic_jseerapu@quicinc.com>
Thu, 25 Sep 2025 12:00:34 +0000 (17:30 +0530)
committerVinod Koul <vkoul@kernel.org>
Thu, 16 Oct 2025 12:17:56 +0000 (17:47 +0530)
GSI hardware generates an interrupt for each transfer completion.
For multiple messages within a single transfer, this results in
N interrupts for N messages, leading to significant software
interrupt latency.

To mitigate this latency, utilize Block Event Interrupt (BEI) mechanism.
Enabling BEI instructs the GSI hardware to prevent interrupt generation
and BEI is disabled when an interrupt is necessary.

Large I2C transfer can be divided into chunks of messages internally.
Interrupts are not expected for the messages for which BEI bit set,
only the last message triggers an interrupt, indicating the completion of
N messages. This BEI mechanism enhances overall transfer efficiency.

Signed-off-by: Jyothi Kumar Seerapu <quic_jseerapu@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/qcom/gpi.c

index 8e87738086b25ac37edbdcd5e237447f3e832e8e..66bfea1f156d1ef58b686380f792d2ecdfb420c1 100644 (file)
@@ -1619,7 +1619,8 @@ gpi_peripheral_config(struct dma_chan *chan, struct dma_slave_config *config)
 }
 
 static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
-                             struct scatterlist *sgl, enum dma_transfer_direction direction)
+                             struct scatterlist *sgl, enum dma_transfer_direction direction,
+                             unsigned long flags)
 {
        struct gpi_i2c_config *i2c = chan->config;
        struct device *dev = chan->gpii->gpi_dev->dev;
@@ -1684,6 +1685,9 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
 
                tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE);
                tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOT);
+
+               if (!(flags & DMA_PREP_INTERRUPT))
+                       tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_BEI);
        }
 
        for (i = 0; i < tre_idx; i++)
@@ -1827,6 +1831,9 @@ gpi_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                return NULL;
        }
 
+       if (!(flags & DMA_PREP_INTERRUPT) && (nr - nr_tre < 2))
+               return NULL;
+
        gpi_desc = kzalloc(sizeof(*gpi_desc), GFP_NOWAIT);
        if (!gpi_desc)
                return NULL;
@@ -1835,7 +1842,7 @@ gpi_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        if (gchan->protocol == QCOM_GPI_SPI) {
                i = gpi_create_spi_tre(gchan, gpi_desc, sgl, direction);
        } else if (gchan->protocol == QCOM_GPI_I2C) {
-               i = gpi_create_i2c_tre(gchan, gpi_desc, sgl, direction);
+               i = gpi_create_i2c_tre(gchan, gpi_desc, sgl, direction, flags);
        } else {
                dev_err(dev, "invalid peripheral: %d\n", gchan->protocol);
                kfree(gpi_desc);