Skip to content

SPI: ESP32: chip-select misbehaviour with multiple gpio-cs #65635

@MrMarteng

Description

@MrMarteng

Describe the bug
There seems to be a race-condition that causes gpio-cs to be not released properly before the next transfer begins. The cs-line stays active and communication to the device interferes. I identified this on esp32c3.

To Reproduce
I first noticed it when using 2 cs-gpios for different devices drivers connected to the same SPI bus.
The application is based on esp32c3_devkitm:

&spi2 {
	#address-cells = <1>;
	#size-cells = <0>;
	status = "okay";
	dma-enabled;
	pinctrl-0 = <&spim2_default>;
	pinctrl-names = "default";
	cs-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>, 	// ST7789V CS
			<&gpio0 1 GPIO_ACTIVE_LOW>;	// trf7977 CS

	st7789v: st7789v@0 {
		compatible = "sitronix,st7789v";
		reg = <0>;
		status = "okay";
		/* ... */
	};

        trf79xx_nfc: ti_trf7970@1 {
		compatible = "ti,trf79xx";
		reg = <1>;
		status = "okay";
                /* ... */
	};

After identifying this issue I wrote a small sample program to manually evaluate the behavior.
In this sample program 2 threads are running and continuously sending data on the SPI bus.
Thread1 sends the 1024 bytes with the value 0xFF every 250 ms. It uses gpio0-1 for chip-select:

config.cs.gpio.port = DEVICE_DT_GET(DT_NODELABEL(gpio0));
config.cs.gpio.pin = 1;
config.cs.gpio.dt_flags = GPIO_ACTIVE_LOW;
config.frequency = 100000;
config.operation = SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_MODE_CPHA | SPI_MODE_CPOL;
gpio_pin_configure(config.cs.gpio.port, config.cs.gpio.pin, GPIO_OUTPUT_INACTIVE | GPIO_ACTIVE_LOW);

memset(data, 0xFFu, sizeof(data));
spi_buf.buf = data;
spi_buf.len = sizeof(data);
spi_bufset.buffers = &spi_buf;
spi_bufset.count = 1;

while (true)
{
    spi_write(pSpi, &config, &spi_bufset);

    k_sleep(K_MSEC(250));
}

Thread1 sends the 512 bytes with the value 0x00 every 1 s. It uses gpio0-10 for chip-select:

memset(data, 0x00u, sizeof(data));
config.cs.gpio.port = DEVICE_DT_GET(DT_NODELABEL(gpio0));
config.cs.gpio.pin = 10;
config.cs.gpio.dt_flags = GPIO_ACTIVE_LOW;
config.frequency = 1000;
config.operation =
    SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_MODE_CPHA | /*SPI_LOCK_ON | SPI_HOLD_ON_CS | */SPI_MODE_CPOL;
gpio_pin_configure(config.cs.gpio.port, config.cs.gpio.pin, GPIO_OUTPUT_INACTIVE | GPIO_ACTIVE_LOW);

while (true)
{
    spi_buf.buf = data;
    spi_buf.len = sizeof(data);
    spi_bufset.buffers = &spi_buf;
    spi_bufset.count = 1;

    spi_write(pSpi, &config, &spi_bufset);

    k_sleep(K_MSEC(1000));
}

I took a scope with my logic analyzer:
Screenshot 2023-11-22 201213
The line D4/IRQ pin is low within the transceive() function in spi_esp32_spim.c.
gpio0-1 is signal "CS"
gpio0-10 is signal "CS2"

Expected behavior
The signal "CS" should only be active (low) when transfer from thread 1 is active. The signal "CS2" should only be active (low) when transfer from thread 2 is active. But as shown in the trace above, they are interfering.

Impact
The impact depends on the devices are affected. It could vary between data corruption and complete denial of service.
In my example the trf7977 spi frontend hangs completely after a while as it couldn't handle random data from the other driver. The chip is no longer responding to any transfer.

Environment (please complete the following information):

  • OS: macOS (Sonoma 14.1.1)
  • Zephyr SDK 0.16.3
  • Zephyr running on latest manifest-rev (90b9809)

Metadata

Metadata

Assignees

Labels

Stalearea: SPISPI busbugThe issue is a bug, or the PR is fixing a bugplatform: ESP32Espressif ESP32priority: lowLow impact/importance bug

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions