summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Clark <james.clark@linaro.org>2025-09-02 13:44:59 +0100
committerMark Brown <broonie@kernel.org>2025-09-03 14:30:34 +0100
commit5cc49b5a36b32a2dba41441ea13b93fb5ea21cfd (patch)
treead2175bd23a99eb5b4ed1c32359d5cd5fb9aaaba
parent7d9baf1e530930e28b45805e3855a4a465a9e36e (diff)
spi: spi-fsl-dspi: Report FIFO overflows as errors
In target mode, the host sending more data than can be consumed would be a common problem for any message exceeding the FIFO or DMA buffer size. Cancel the whole message as soon as this condition is hit as the message will be corrupted. Only do this for target mode in a DMA transfer, it's not likely these flags will be set in host mode so it's not worth adding extra checks. In IRQ and polling modes we use the same transfer functions for hosts and targets so the error flags always get checked. This is slightly inconsistent but it's not worth doing the check conditionally because it may catch some host programming errors in the future. Signed-off-by: James Clark <james.clark@linaro.org> Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com> Message-ID: <20250902-james-nxp-spi-dma-v6-7-f7aa2c5e56e2@linaro.org> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--drivers/spi/spi-fsl-dspi.c28
1 files changed, 27 insertions, 1 deletions
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 3d29285c772c..83ea296597e9 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -480,6 +480,17 @@ static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata)
dspi->dev_to_host(dspi, rxdata);
}
+static int dspi_fifo_error(struct fsl_dspi *dspi, u32 spi_sr)
+{
+ if (spi_sr & (SPI_SR_TFUF | SPI_SR_RFOF)) {
+ dev_err_ratelimited(&dspi->pdev->dev, "FIFO errors:%s%s\n",
+ spi_sr & SPI_SR_TFUF ? " TX underflow," : "",
+ spi_sr & SPI_SR_RFOF ? " RX overflow," : "");
+ return -EIO;
+ }
+ return 0;
+}
+
#if IS_ENABLED(CONFIG_DMA_ENGINE)
/* Prepare one TX FIFO entry (txdata plus cmd) */
@@ -553,6 +564,7 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
struct device *dev = &dspi->pdev->dev;
struct fsl_dspi_dma *dma = dspi->dma;
int time_left;
+ u32 spi_sr;
int i;
for (i = 0; i < dspi->words_in_flight; i++)
@@ -603,7 +615,8 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
if (spi_controller_is_target(dspi->ctlr)) {
wait_for_completion_interruptible(&dspi->dma->cmd_rx_complete);
- return 0;
+ regmap_read(dspi->regmap, SPI_SR, &spi_sr);
+ return dspi_fifo_error(dspi, spi_sr);
}
time_left = wait_for_completion_timeout(&dspi->dma->cmd_tx_complete,
@@ -1073,6 +1086,10 @@ static void dspi_poll(struct fsl_dspi *dspi)
for (tries = 1000; tries > 0; --tries) {
regmap_read(dspi->regmap, SPI_SR, &spi_sr);
regmap_write(dspi->regmap, SPI_SR, spi_sr);
+
+ dspi->cur_msg->status = dspi_fifo_error(dspi, spi_sr);
+ if (dspi->cur_msg->status)
+ return;
if (spi_sr & SPI_SR_CMDTCF)
break;
}
@@ -1088,6 +1105,7 @@ static void dspi_poll(struct fsl_dspi *dspi)
static irqreturn_t dspi_interrupt(int irq, void *dev_id)
{
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
+ int status;
u32 spi_sr;
regmap_read(dspi->regmap, SPI_SR, &spi_sr);
@@ -1096,6 +1114,14 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id)
if (!(spi_sr & SPI_SR_CMDTCF))
return IRQ_NONE;
+ status = dspi_fifo_error(dspi, spi_sr);
+ if (status) {
+ if (dspi->cur_msg)
+ WRITE_ONCE(dspi->cur_msg->status, status);
+ complete(&dspi->xfer_done);
+ return IRQ_HANDLED;
+ }
+
if (dspi_rxtx(dspi) == false) {
if (dspi->cur_msg)
WRITE_ONCE(dspi->cur_msg->status, 0);