]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
spi: stm32: perform small transfer in polling mode
authorAlain Volmat <alain.volmat@foss.st.com>
Thu, 18 Dec 2025 10:48:29 +0000 (11:48 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 18 Dec 2025 10:59:34 +0000 (10:59 +0000)
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 <alain.volmat@foss.st.com>
Link: https://patch.msgid.link/20251218-stm32-spi-enhancements-v2-3-3b69901ca9fe@foss.st.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-stm32.c

index ced6d7b215b9ae5c0c4a2967d96bd61e19141293..8f8968383ad04e309a98dd9276ae8a67e3e18804 100644 (file)
 #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 @@ out:
        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,