TX1 SPI cs-inactive-cycles not being used

Hello,

Recently I was trying to use SPI (spidev) to communicate with an MCU. The MCU needs the SPI CS to be pulled inactive at the end of every byte. I remembered that there was an option nvidia,cs-inactive-cycles in the device tree to define how many cycles CS should be inactive between frames.

Looking at the X1 TRM, I know this should map into the SPI CS Timing2 Register which supports this exact behavior. I’ve used that register in a custom SPI driver I wrote and I know it works. However, even after setting nvidia,cs-inactive-cycles as follows, I see the CS being kept low for the whole transfer.

 spi0_0 {
                    #address-cells = <0x1>;
                    #size-cells = <0x0>;
                    compatible = "spidev";
                    reg = <0x0>;
                    spi-max-frequency = <20000000>;
                    controller-data {
                           nvidia,enable-hw-based-cs;
                           nvidia,cs-setup-clk-count = <0x1e>;
                           nvidia,cs-hold-clk-count = <0x1e>;
                           nvidia,rx-clk-tap-delay = <0x1f>;
                           nvidia,tx-clk-tap-delay = <0x0>;
                           nvidia,cs-inactive-cycles = <0x6>;
                    };
            };

I captured a transmission sent using this command below

./spidev -v -D /dev/spidev0.0 -s 100000 -p "v0000000000000000000000000000000"

Looking into the SPI master controller drivers (bus) source code (this is from L4T 32.4.2 ), I see some interesting logic.

static void tegra_spi_set_timing2(struct spi_device *spi)
{
  struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
  struct tegra_spi_client_ctl_data *cdata = spi->controller_data;
  u32 spi_cs_timing2 = 0;

  if (!cdata || tspi->prod_list)
    return;
  if (!cdata->clk_delay_between_packets)
    return;
  if (cdata->cs_inactive_cycles) {
    u32 inactive_cycles;

    SPI_SET_CS_ACTIVE_BETWEEN_PACKETS(spi_cs_timing2,
              spi->chip_select,
              0);
    inactive_cycles = min(cdata->cs_inactive_cycles, 32);
    SPI_SET_CYCLES_BETWEEN_PACKETS(spi_cs_timing2,
                 spi->chip_select,
                 inactive_cycles);
    if (tspi->spi_cs_timing2 != spi_cs_timing2) {
      tspi->spi_cs_timing2 = spi_cs_timing2;
      tegra_spi_writel(tspi, spi_cs_timing2,
           SPI_CS_TIMING2);
    }
    tspi->is_hw_based_cs = true;
  } else {
    SPI_SET_CS_ACTIVE_BETWEEN_PACKETS(spi_cs_timing2,
              spi->chip_select, 1);
    SPI_SET_CYCLES_BETWEEN_PACKETS(spi_cs_timing2,
                 spi->chip_select, 0);
    if (tspi->spi_cs_timing2 != spi_cs_timing2) {
      tspi->spi_cs_timing2 = spi_cs_timing2;
      tegra_spi_writel(tspi, spi_cs_timing2,
           SPI_CS_TIMING2);
    }
  }
}

It seems if cdata->clk_delay_between_packets isn’t set, then SPI CS Timing2 Register isn’t updated at all. That logic doesn’t make sense at all and that if-statement causes nvidia,cs-inactive-cycles to be ineffective by itself.

Am I missing something here?

Oh I just figured this out it’s a bug in the NVIDIA SPI driver.

And it hasn’t been patched since 2017! facepalm🤦‍♂️