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?