[PATCH v2] spi: stm32: enable controller before asserting CS

Leonard Göhrs l.goehrs at pengutronix.de
Mon May 13 01:29:49 PDT 2024


Hi,

I am in the process of updating an STM32MP157 based device from 6.8 to 6.9
and have noticed SPI related issues that may be caused by this change.

I am testing on an LXA TAC Generation 2 (arch/arm/boot/dts/st/stm32mp157c-lxa-tac-gen2.dts)
and the issues I see are SPI transfer timeouts:

   [   13.565081] panel-mipi-dbi-spi spi2.0: SPI transfer timed out
   [   13.565131] spi_master spi2: failed to transfer one message from queue
   [   13.565134] spi_stm32 44005000.spi: spurious IT (sr=0x00010002, ier=0x00000000)
   [   13.565145] spi_master spi2: noqueue transfer failed
   [   13.565183] panel-mipi-dbi-spi spi2.0: error -110 when sending command 0x2a
   [   13.769113] panel-mipi-dbi-spi spi2.0: SPI transfer timed out
   [   13.769163] spi_master spi2: failed to transfer one message from queue
   [   13.769164] spi_stm32 44005000.spi: spurious IT (sr=0x00010002, ier=0x00000000)
   [   13.769177] spi_master spi2: noqueue transfer failed
   [   13.769210] panel-mipi-dbi-spi spi2.0: error -110 when sending command 0x2b
   [   13.977028] panel-mipi-dbi-spi spi2.0: SPI transfer timed out
   [   13.977082] spi_master spi2: failed to transfer one message from queue
   [   13.977095] spi_master spi2: noqueue transfer failed
   [   14.460924] panel-mipi-dbi-spi spi2.0: SPI transfer timed out

Followed by workqueue lockups and the device becoming unresponsive later
in the boot, preventing me from logging in and investigating further that way:

   [   17.026263] spi_master spi2: noqueue transfer failed

   TAC OS - The LXA TAC operating system 24.04+dev lxatac-00011 ttySTM0

   lxatac-00011 login: root
   [   62.434326] BUG: workqueue lockup - pool cpus=0 node=0 flags=0x0 nice=0 stuck for 44s!
   [   62.441321] BUG: workqueue lockup - pool cpus=0 node=0 flags=0x0 nice=-20 stuck for 44s!
   …

Reverting this commit fixes the issue for me. It may be some time before
I get around to investigating the issue in detail, so I thought I should
ask if anyone else has already noticed this as well.

We are currently in the process of adding the device in question to
KernelCI [1], which may help in catching such problems earlier.

[1]: https://github.com/kernelci/kernelci-core/pull/2542


On 24.04.24 15:52, Ben Wolsieffer wrote:
> On the STM32F4/7, the MOSI and CLK pins float while the controller is
> disabled. CS is a regular GPIO, and therefore always driven. Currently,
> the controller is enabled in the transfer_one() callback, which runs
> after CS is asserted.  Therefore, there is a period where the SPI pins
> are floating while CS is asserted, making it possible for stray signals
> to disrupt communications. An analogous problem occurs at the end of the
> transfer when the controller is disabled before CS is released.
> 
> This problem can be reliably observed by enabling the pull-up (if
> CPOL=0) or pull-down (if CPOL=1) on the clock pin. This will cause two
> extra unintended clock edges per transfer, when the controller is
> enabled and disabled.
> 
> Note that this bug is likely not present on the STM32H7, because this
> driver sets the AFCNTR bit (not supported on F4/F7), which keeps the SPI
> pins driven even while the controller is disabled.
> 
> Enabling/disabling the controller as part of runtime PM was suggested as
> an alternative approach, but this breaks the driver on the STM32MP1 (see
> [1]). The following quote from the manual may explain this:
> 
>> To restart the internal state machine properly, SPI is strongly
>> suggested to be disabled and re-enabled before next transaction starts
>> despite its setting is not changed.
> 
> This patch has been tested on an STM32F746 with a MAX14830 UART
> expander.
> 
> [1] https://lore.kernel.org/lkml/ZXzRi_h2AMqEhMVw@dell-precision-5540/T/
> 
> Signed-off-by: Ben Wolsieffer <ben.wolsieffer at hefring.com>
> ---
> v2:
>   * Improve explanation of problem
>   * Discuss why not to use runtime PM instead
> 
>   drivers/spi/spi-stm32.c | 14 ++------------
>   1 file changed, 2 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c
> index e4e7ddb7524a..4a68abcdcc35 100644
> --- a/drivers/spi/spi-stm32.c
> +++ b/drivers/spi/spi-stm32.c
> @@ -1016,10 +1016,8 @@ static irqreturn_t stm32fx_spi_irq_event(int irq, void *dev_id)
>   static irqreturn_t stm32fx_spi_irq_thread(int irq, void *dev_id)
>   {
>   	struct spi_controller *ctrl = dev_id;
> -	struct stm32_spi *spi = spi_controller_get_devdata(ctrl);
>   
>   	spi_finalize_current_transfer(ctrl);
> -	stm32fx_spi_disable(spi);
>   
>   	return IRQ_HANDLED;
>   }
> @@ -1187,6 +1185,8 @@ static int stm32_spi_prepare_msg(struct spi_controller *ctrl,
>   			 ~clrb) | setb,
>   			spi->base + spi->cfg->regs->cpol.reg);
>   
> +	stm32_spi_enable(spi);
> +
>   	spin_unlock_irqrestore(&spi->lock, flags);
>   
>   	return 0;
> @@ -1204,7 +1204,6 @@ static void stm32fx_spi_dma_tx_cb(void *data)
>   
>   	if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) {
>   		spi_finalize_current_transfer(spi->ctrl);
> -		stm32fx_spi_disable(spi);
>   	}
>   }
>   
> @@ -1219,7 +1218,6 @@ static void stm32_spi_dma_rx_cb(void *data)
>   	struct stm32_spi *spi = data;
>   
>   	spi_finalize_current_transfer(spi->ctrl);
> -	spi->cfg->disable(spi);
>   }
>   
>   /**
> @@ -1307,8 +1305,6 @@ static int stm32fx_spi_transfer_one_irq(struct stm32_spi *spi)
>   
>   	stm32_spi_set_bits(spi, STM32FX_SPI_CR2, cr2);
>   
> -	stm32_spi_enable(spi);
> -
>   	/* starting data transfer when buffer is loaded */
>   	if (spi->tx_buf)
>   		spi->cfg->write_tx(spi);
> @@ -1345,8 +1341,6 @@ static int stm32h7_spi_transfer_one_irq(struct stm32_spi *spi)
>   
>   	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);
> @@ -1378,8 +1372,6 @@ static void stm32fx_spi_transfer_one_dma_start(struct stm32_spi *spi)
>   		 */
>   		stm32_spi_set_bits(spi, STM32FX_SPI_CR2, STM32FX_SPI_CR2_ERRIE);
>   	}
> -
> -	stm32_spi_enable(spi);
>   }
>   
>   /**
> @@ -1413,8 +1405,6 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
>   
>   	stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier);
>   
> -	stm32_spi_enable(spi);
> -
>   	if (STM32_SPI_HOST_MODE(spi))
>   		stm32_spi_set_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_CSTART);
>   }



More information about the linux-arm-kernel mailing list