From c266d19b7d4e5ed993ed9fca25ab9d11789c41ee Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Thu, 18 Dec 2025 11:48:27 +0100 Subject: [PATCH 1/3] spi: stm32: properly fail on dma_request_chan error Correct handling of the dma_request_chan call in order to avoid misleading warn message if no DMA is provided within the device-tree and moreover fail if dma_request_chan has returned a valid error. Signed-off-by: Alain Volmat Link: https://patch.msgid.link/20251218-stm32-spi-enhancements-v2-1-3b69901ca9fe@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 2c804c1aef98..40651b6050f6 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2406,11 +2406,13 @@ static int stm32_spi_probe(struct platform_device *pdev) spi->dma_tx = dma_request_chan(spi->dev, "tx"); if (IS_ERR(spi->dma_tx)) { ret = PTR_ERR(spi->dma_tx); - spi->dma_tx = NULL; - if (ret == -EPROBE_DEFER) + if (ret == -ENODEV) { + dev_info(&pdev->dev, "tx dma disabled\n"); + spi->dma_tx = NULL; + } else { + dev_err_probe(&pdev->dev, ret, "failed to request tx dma channel\n"); goto err_clk_disable; - - dev_warn(&pdev->dev, "failed to request tx dma channel\n"); + } } else { ctrl->dma_tx = spi->dma_tx; } @@ -2418,11 +2420,13 @@ static int stm32_spi_probe(struct platform_device *pdev) spi->dma_rx = dma_request_chan(spi->dev, "rx"); if (IS_ERR(spi->dma_rx)) { ret = PTR_ERR(spi->dma_rx); - spi->dma_rx = NULL; - if (ret == -EPROBE_DEFER) + if (ret == -ENODEV) { + dev_info(&pdev->dev, "rx dma disabled\n"); + spi->dma_rx = NULL; + } else { + dev_err_probe(&pdev->dev, ret, "failed to request rx dma channel\n"); goto err_dma_release; - - dev_warn(&pdev->dev, "failed to request rx dma channel\n"); + } } else { ctrl->dma_rx = spi->dma_rx; } From 1ac3be217c01d5df55ec5052f81e4f1708f46552 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Date: Thu, 18 Dec 2025 11:48:28 +0100 Subject: [PATCH 2/3] spi: stm32: fix Overrun issue at < 8bpw When SPI communication is suspended by hardware automatically, it could happen that few bits of next frame are already clocked out due to internal synchronization delay. To achieve a safe suspension, we need to ensure that each word must be at least 8 SPI clock cycles long. That's why, if bpw is less than 8 bits, we need to use midi to reach 8 SPI clock cycles at least. This will ensure that each word achieve safe suspension and prevent overrun condition. Signed-off-by: Deepak Kumar Signed-off-by: Alain Volmat Link: https://patch.msgid.link/20251218-stm32-spi-enhancements-v2-2-3b69901ca9fe@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 40651b6050f6..ced6d7b215b9 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1906,11 +1906,12 @@ static void stm32h7_spi_data_idleness(struct stm32_spi *spi, struct spi_transfer cfg2_clrb |= STM32H7_SPI_CFG2_MIDI; if ((len > 1) && (spi->cur_midi > 0)) { u32 sck_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, spi->cur_speed); - u32 midi = min_t(u32, - DIV_ROUND_UP(spi->cur_midi, sck_period_ns), - FIELD_GET(STM32H7_SPI_CFG2_MIDI, - STM32H7_SPI_CFG2_MIDI)); + u32 midi = DIV_ROUND_UP(spi->cur_midi, sck_period_ns); + if ((spi->cur_bpw + midi) < 8) + midi = 8 - spi->cur_bpw; + + midi = min_t(u32, midi, FIELD_MAX(STM32H7_SPI_CFG2_MIDI)); dev_dbg(spi->dev, "period=%dns, midi=%d(=%dns)\n", sck_period_ns, midi, midi * sck_period_ns); From b39ef93a2e5b5f4289a3486d8a94a09a1e6a4c67 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Thu, 18 Dec 2025 11:48:29 +0100 Subject: [PATCH 3/3] spi: stm32: perform small transfer in polling mode In case of interrupt based transfer, when the transfer is very small, relying on interrupts leads to lower performances than if the transfer were done using polling on the registers. Add a module parameter "polling_limit_us" to indicate the threshold in us from which a transfer would be done polling the registers rather than relying on interrupts. Signed-off-by: Alain Volmat Link: https://patch.msgid.link/20251218-stm32-spi-enhancements-v2-3-3b69901ca9fe@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index ced6d7b215b9..8f8968383ad0 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -202,6 +202,10 @@ #define STM32_SPI_HOST_MODE(stm32_spi) (!(stm32_spi)->device_mode) #define STM32_SPI_DEVICE_MODE(stm32_spi) ((stm32_spi)->device_mode) +static unsigned int polling_limit_us = 30; +module_param(polling_limit_us, uint, 0664); +MODULE_PARM_DESC(polling_limit_us, "maximum time in us to run a transfer in polling mode\n"); + /** * struct stm32_spi_reg - stm32 SPI register & bitfield desc * @reg: register offset @@ -266,6 +270,7 @@ struct stm32_spi; * @dma_rx_cb: routine to call after DMA RX channel operation is complete * @dma_tx_cb: routine to call after DMA TX channel operation is complete * @transfer_one_irq: routine to configure interrupts for driver + * @transfer_one_poll: routine to perform a transfer via register polling * @irq_handler_event: Interrupt handler for SPI controller events * @irq_handler_thread: thread of interrupt handler for SPI controller * @baud_rate_div_min: minimum baud rate divisor @@ -291,6 +296,7 @@ struct stm32_spi_cfg { void (*dma_rx_cb)(void *data); void (*dma_tx_cb)(void *data); int (*transfer_one_irq)(struct stm32_spi *spi); + int (*transfer_one_poll)(struct stm32_spi *spi); irqreturn_t (*irq_handler_event)(int irq, void *dev_id); irqreturn_t (*irq_handler_thread)(int irq, void *dev_id); unsigned int baud_rate_div_min; @@ -1355,6 +1361,55 @@ static int stm32fx_spi_transfer_one_irq(struct stm32_spi *spi) return 1; } +/** + * stm32h7_spi_transfer_one_poll - transfer a single spi_transfer by direct + * register access without interrupt usage + * @spi: pointer to the spi controller data structure + * + * It must returns 0 if the transfer is finished or 1 if the transfer is still + * in progress. + */ +static int stm32h7_spi_transfer_one_poll(struct stm32_spi *spi) +{ + unsigned long flags; + u32 sr; + + spin_lock_irqsave(&spi->lock, flags); + + stm32_spi_enable(spi); + + /* Be sure to have data in fifo before starting data transfer */ + if (spi->tx_buf) + stm32h7_spi_write_txfifo(spi); + + if (STM32_SPI_HOST_MODE(spi)) + stm32_spi_set_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_CSTART); + + sr = readl_relaxed(spi->base + STM32H7_SPI_SR); + /* Keep writing / reading while waiting for the end of transfer */ + while (spi->tx_len || spi->rx_len || !(sr & STM32H7_SPI_SR_EOT)) { + if (spi->rx_len && (sr & (STM32H7_SPI_SR_RXP | STM32H7_SPI_SR_RXWNE | + STM32H7_SPI_SR_RXPLVL))) + stm32h7_spi_read_rxfifo(spi); + + if (spi->tx_len && (sr & STM32H7_SPI_SR_TXP)) + stm32h7_spi_write_txfifo(spi); + + sr = readl_relaxed(spi->base + STM32H7_SPI_SR); + + /* Clear suspension bit if necessary */ + if (sr & STM32H7_SPI_SR_SUSP) + writel_relaxed(sr & STM32H7_SPI_SR_SUSP, spi->base + STM32H7_SPI_IFCR); + } + + spin_unlock_irqrestore(&spi->lock, flags); + + stm32h7_spi_disable(spi); + spi_finalize_current_transfer(spi->ctrl); + + return 0; +} + /** * stm32h7_spi_transfer_one_irq - transfer a single spi_transfer using * interrupts @@ -2026,6 +2081,24 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, return ret; } +/** + * stm32_spi_can_poll - detect if poll based transfer is appropriate + * @spi: pointer to the spi controller data structure + * + * Returns true is poll is more appropriate, false otherwise. + */ +static bool stm32_spi_can_poll(struct stm32_spi *spi) +{ + unsigned long hz_per_byte, byte_limit; + + /* Evaluate the transfer time and use polling if applicable */ + hz_per_byte = polling_limit_us ? + DIV_ROUND_UP(8 * USEC_PER_SEC, polling_limit_us) : 0; + byte_limit = hz_per_byte ? spi->cur_speed / hz_per_byte : 1; + + return (spi->cur_xferlen < byte_limit) ? true : false; +} + /** * stm32_spi_transfer_one - transfer a single spi_transfer * @ctrl: controller interface @@ -2058,6 +2131,8 @@ static int stm32_spi_transfer_one(struct spi_controller *ctrl, if (spi->cur_usedma) return stm32_spi_transfer_one_dma(spi, transfer); + else if (spi->cfg->transfer_one_poll && stm32_spi_can_poll(spi)) + return spi->cfg->transfer_one_poll(spi); else return spi->cfg->transfer_one_irq(spi); } @@ -2216,6 +2291,7 @@ static const struct stm32_spi_cfg stm32h7_spi_cfg = { * SPI access hence handling is performed within the SPI interrupt */ .transfer_one_irq = stm32h7_spi_transfer_one_irq, + .transfer_one_poll = stm32h7_spi_transfer_one_poll, .irq_handler_thread = stm32h7_spi_irq_thread, .baud_rate_div_min = STM32H7_SPI_MBR_DIV_MIN, .baud_rate_div_max = STM32H7_SPI_MBR_DIV_MAX, @@ -2245,6 +2321,7 @@ static const struct stm32_spi_cfg stm32mp25_spi_cfg = { * SPI access hence handling is performed within the SPI interrupt */ .transfer_one_irq = stm32h7_spi_transfer_one_irq, + .transfer_one_poll = stm32h7_spi_transfer_one_poll, .irq_handler_thread = stm32h7_spi_irq_thread, .baud_rate_div_min = STM32H7_SPI_MBR_DIV_MIN, .baud_rate_div_max = STM32H7_SPI_MBR_DIV_MAX,