]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
spi: spi-fsl-dspi: Clear completion counter before initiating transfer
authorJames Clark <james.clark@linaro.org>
Fri, 27 Jun 2025 10:21:37 +0000 (11:21 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Jul 2025 16:25:00 +0000 (18:25 +0200)
[ Upstream commit fa60c094c19b97e103d653f528f8d9c178b6a5f5 ]

In target mode, extra interrupts can be received between the end of a
transfer and halting the module if the host continues sending more data.
If the interrupt from this occurs after the reinit_completion() then the
completion counter is left at a non-zero value. The next unrelated
transfer initiated by userspace will then complete immediately without
waiting for the interrupt or writing to the RX buffer.

Fix it by resetting the counter before the transfer so that lingering
values are cleared. This is done after clearing the FIFOs and the
status register but before the transfer is initiated, so no interrupts
should be received at this point resulting in other race conditions.

Fixes: 4f5ee75ea171 ("spi: spi-fsl-dspi: Replace interruptible wait queue with a simple completion")
Signed-off-by: James Clark <james.clark@linaro.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20250627-james-nxp-spi-dma-v4-1-178dba20c120@linaro.org
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/spi/spi-fsl-dspi.c

index 7b62ecbe36321cbabb3cb9c1e679bc75e3185d36..de203b9e3f1b8a6c2eb16367ba3e05094932eb57 100644 (file)
@@ -773,10 +773,28 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
                trans_mode = dspi->devtype_data->trans_mode;
                switch (trans_mode) {
                case DSPI_EOQ_MODE:
+                       /*
+                        * Reinitialize the completion before transferring data
+                        * to avoid the case where it might remain in the done
+                        * state due to a spurious interrupt from a previous
+                        * transfer. This could falsely signal that the current
+                        * transfer has completed.
+                        */
+                       if (dspi->irq)
+                               reinit_completion(&dspi->xfer_done);
                        regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
                        dspi_eoq_write(dspi);
                        break;
                case DSPI_TCFQ_MODE:
+                       /*
+                        * Reinitialize the completion before transferring data
+                        * to avoid the case where it might remain in the done
+                        * state due to a spurious interrupt from a previous
+                        * transfer. This could falsely signal that the current
+                        * transfer has completed.
+                        */
+                       if (dspi->irq)
+                               reinit_completion(&dspi->xfer_done);
                        regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
                        dspi_tcfq_write(dspi);
                        break;
@@ -796,7 +814,6 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
                if (trans_mode != DSPI_DMA_MODE) {
                        if (dspi->irq) {
                                wait_for_completion(&dspi->xfer_done);
-                               reinit_completion(&dspi->xfer_done);
                        } else {
                                do {
                                        status = dspi_poll(dspi);