From 8980bc573ae2d2b9d86d76aeb86c76e1597070bf Mon Sep 17 00:00:00 2001 From: Carlos Jones Jr Date: Fri, 2 Oct 2026 16:55:40 +0800 Subject: [PATCH 1/4] spi: tegra114: assert CS GPIO for every transfer in a message The driver provides its own transfer_one_message() and therefore drives the chip-select GPIO itself rather than through the SPI core. The GPIO is asserted only in the is_first_of_msg path of tegra_spi_setup_transfer_one(), but tegra_spi_transfer_end() deasserts it whenever a transfer has cs_change set. For a multi-transfer message whose earlier transfers request cs_change -- for example a register read that sends the address in one transfer and clocks the data back in the next -- the CS GPIO is released after the first transfer and never re-asserted, so the remaining transfers are clocked with chip-select inactive and the peripheral never drives the bus. This was observed with an ADIS16470 IMU (adis16475 driver), whose register read is a two-transfer, cs_change-framed exchange: a logic analyzer showed the data frame clocked with CS high, and the driver read 0 while the device drove 0x4056 on the wire. Re-assert the CS GPIO for every transfer so that multi-transfer messages keep chip-select active across the whole message. gpiod_set_value() is idempotent and polarity-aware, so this is a no-op for the first transfer and for messages that keep CS asserted throughout. Fixes: 63c1440596ff ("spi: tegra114: add support for gpio based CS") Signed-off-by: Carlos Jones Jr Link: https://patch.msgid.link/20261002085540.1625-1-carlosjr.jones@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 333fb549214e..5f8d8da7a3fc 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -859,6 +859,13 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, command1 |= SPI_BIT_LENGTH(bits_per_word - 1); } + /* + * Re-assert the CS GPIO for every transfer: transfer_end() drops it + * on cs_change, and the assert above only covers is_first_of_msg. + */ + if (spi_get_csgpiod(spi, 0)) + gpiod_set_value(spi_get_csgpiod(spi, 0), 1); + return command1; } From 59eb22641f0c526dd064390b11cff99ae87c2858 Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Thu, 15 Oct 2026 22:58:00 +0200 Subject: [PATCH 2/4] spi: imx: fix slab corruption from undersized DMA bounce buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit faa8e404ad8e ("spi: imx: support dynamic burst length for ECSPI DMA mode"), the driver allocates its private DMA TX/RX buffers with kzalloc(dma_len), where dma_len is only rounded up to 4 bytes, but maps, syncs and unmaps them with DMA_CACHE_ALIGNED_LEN(dma_len), i.e. rounded up to the cache line size. As a result, the DMA API operates on memory beyond the end of the allocation. This was observed on a custom i.MX8MP platform with an mcp251xfd CAN controller under heavy RPMSG load, as a series of refcount_t warnings triggered from rpmsg_recv_done() in virtio_rpmsg_bus: refcount_t: addition on 0; use-after-free. refcount_t: underflow; use-after-free. refcount_t: saturated; leaking memory. Leading finally to an "Unable to handle kernel paging request" oops. Reproducing this problem took between 30 minutes and roughly 30 hours under the same workload. Hardware watchpoints on the refcount of a struct rpmsg_endpoint showed it was overwritten from 2 to 1 by __pi_memcpy_generic() in the irq/155-spi2.0 thread. Additionally it was found that a "normal" rpmsg_recv_done() took 11µs +/-2µs from its kref_get to kref_put. In the error case the same sequence took about 215µs +/-10µs. Based on this, an LLM was used to identify the size mismatch fixed here. Allocate the buffers with DMA_CACHE_ALIGNED_LEN(dma_len) so they cover the full mapped length, and use the same length for dma_sync_single_for_cpu() to stay consistent with map and unmap. With this patch applied, the same workload ran for more than 200 hours without the problem reproducing. Assisted-by: LLM Fixes: faa8e404ad8e ("spi: imx: support dynamic burst length for ECSPI DMA mode") Cc: stable@vger.kernel.org Signed-off-by: Richard Leitner Link: https://patch.msgid.link/20261015-fix-spi-imx-dma-v1-1-e28259671392@linux.dev Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 79a6c1a60b0a..3851c50dbe31 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1578,11 +1578,13 @@ static int spi_imx_dma_tx_data_handle(struct spi_imx_data *spi_imx, dma_data->dma_len = ALIGN(dma_data->data_len, BYTES_PER_32BITS_WORD); } - dma_data->dma_tx_buf = kzalloc(dma_data->dma_len, GFP_KERNEL); + dma_data->dma_tx_buf = kzalloc(DMA_CACHE_ALIGNED_LEN(dma_data->dma_len), + GFP_KERNEL); if (!dma_data->dma_tx_buf) return -ENOMEM; - dma_data->dma_rx_buf = kzalloc(dma_data->dma_len, GFP_KERNEL); + dma_data->dma_rx_buf = kzalloc(DMA_CACHE_ALIGNED_LEN(dma_data->dma_len), + GFP_KERNEL); if (!dma_data->dma_rx_buf) { kfree(dma_data->dma_tx_buf); return -ENOMEM; @@ -1920,7 +1922,8 @@ static int spi_imx_dma_package_transfer(struct spi_imx_data *spi_imx, /* Trim the DMA RX buffer and copy the actual data to rx_buf */ dma_sync_single_for_cpu(controller->dma_rx->device->dev, dma_data->dma_rx_addr, - dma_data->dma_len, DMA_FROM_DEVICE); + DMA_CACHE_ALIGNED_LEN(dma_data->dma_len), + DMA_FROM_DEVICE); spi_imx_dma_rx_data_handle(spi_imx, dma_data, transfer->rx_buf + spi_imx->rx_offset, word_delay); spi_imx->rx_offset += dma_data->data_len; From 4eac392e48fc0ddc6d5ac10d57aac7245c17d0d9 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 9 Oct 2026 12:16:35 +0200 Subject: [PATCH 3/4] spi: spi-fsl-lpspi: pass struct fsl_lpspi_data instead of struct spi_controller if possible Instead of passing a spi_controller pointer to functions and using spi_controller_get_devdata() to get the struct fsl_lpspi_data pointer, directly pass a struct fsl_lpspi_data pointer. Signed-off-by: Marc Kleine-Budde Reviewed-by: Frank Li Link: https://patch.msgid.link/20261009-spi-fsl-lpspi-more-cleanups-v1-1-2d4549d5205e@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 5aa0a75b4e57..a3c5bab1acc7 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -550,11 +550,8 @@ static int fsl_lpspi_target_abort(struct spi_controller *controller) return 0; } -static int fsl_lpspi_wait_for_completion(struct spi_controller *controller) +static int fsl_lpspi_wait_for_completion(struct fsl_lpspi_data *fsl_lpspi) { - struct fsl_lpspi_data *fsl_lpspi = - spi_controller_get_devdata(controller); - if (fsl_lpspi->is_target) { if (wait_for_completion_interruptible(&fsl_lpspi->xfer_done) || fsl_lpspi->target_aborted) { @@ -758,11 +755,9 @@ static int fsl_lpspi_dma_init(struct device *dev, return ret; } -static int fsl_lpspi_pio_transfer(struct spi_controller *controller, +static int fsl_lpspi_pio_transfer(struct fsl_lpspi_data *fsl_lpspi, struct spi_transfer *t) { - struct fsl_lpspi_data *fsl_lpspi = - spi_controller_get_devdata(controller); int ret; fsl_lpspi->tx_buf = t->tx_buf; @@ -774,7 +769,7 @@ static int fsl_lpspi_pio_transfer(struct spi_controller *controller, fsl_lpspi_write_tx_fifo(fsl_lpspi); - ret = fsl_lpspi_wait_for_completion(controller); + ret = fsl_lpspi_wait_for_completion(fsl_lpspi); fsl_lpspi_reset(fsl_lpspi); @@ -803,7 +798,7 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller, if (fsl_lpspi->usedma) ret = fsl_lpspi_dma_transfer(controller, fsl_lpspi, t); else - ret = fsl_lpspi_pio_transfer(controller, t); + ret = fsl_lpspi_pio_transfer(fsl_lpspi, t); if (ret < 0) return ret; From b0f0c761ed3fa10754dc9295db85374a173123b7 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 9 Oct 2026 12:16:36 +0200 Subject: [PATCH 4/4] spi: spi-fsl-lpspi: directly return ret in fsl_lpspi_transfer_one() Both fsl_lpspi_dma_transfer() and fsl_lpspi_pio_transfer() only return negative error values in case of error or 0 in case of success. Directly return ret, there is no need to check for positive error values and return them as 0. Signed-off-by: Marc Kleine-Budde Link: https://patch.msgid.link/20261009-spi-fsl-lpspi-more-cleanups-v1-2-2d4549d5205e@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index a3c5bab1acc7..b25f94b2ef73 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -799,10 +799,8 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller, ret = fsl_lpspi_dma_transfer(controller, fsl_lpspi, t); else ret = fsl_lpspi_pio_transfer(fsl_lpspi, t); - if (ret < 0) - return ret; - return 0; + return ret; } static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)