SPI transfer API time cost issue

Hi everyone,
I use spi_sync to read data from an ADC device. The code as follow:

void test(void)
{
    int i, ret, miss_count=1;
    spi_message_init(&m);
    spi_message_add_tail(&tr, &m);
    for (i = 0; i < 40000; i++)
    {

        mutex_lock(&ads1299.buf_lock);

        calltime = ktime_get();
        ret = spi_sync(ads1299.spi, &m);
        rettime = ktime_get();

        /*calculate time cost*/
        delta = ktime_sub(rettime, calltime);
        duration = (unsigned long long )ktime_to_ns(delta) >> 10;
        
        if (duration > 200)
        {
            printk("%d miss, time is %lld us\r\n", miss_count,duration);
            miss_count++;
        }
        
        
        mutex_unlock(&ads1299.buf_lock);
    }
    printk("done\r\n");
}

The output is:


You can see sometimes the time up to 2000us. Then I try spi_async and found it’s performance is worse.

Cause I can read data from ADC when the ADC give a ready signal to me, and that signal will trigger a interrupt.I need finish spi data transfer between the 2 interrupts interval, for example, if I use 4kHz sample rate, I should finish the data read less than 250us, otherwise I will lose data.

I check the hardware time cost(CS low to high), it cost less than 50us, so I think the time cost may realted to the SPI controller driver.

I think about the DMA, and find this tipic How SPI can use DMA on Xavier! , ShaneCCC said when use these system api, use the DMA default.

So the question is :

  1. Can I reduce the time cost when I just use the system api (spi_sync, spi_async).
  2. How the SPI work with DMA? Can DMA control the SPI transfer directly?That is to say, the interrupt trigger the DMA, DMA read data use SPI, and when the data is enough, DMA trigger a callback to let CPU handle the data. If I want realize the idea, should I change the NVIDIA spi controller driver?
  3. Do you have any other idea about my requestment?

Thanks a lot.

Could you try polling mode by add below to device tree. Also boos the system by

sudo nvpmodel -m 0
sudo jetson_clocks

Have detail information from …/kernel/kernel-4.9/Documentation/devicetree/bindings/spi/nvidia%2Ctegra114-spi.txt

nvidia,polling-mode, nvidia,disable-runtime-pm

Hi, Shane
This is my dt config

spi@7000d400 {
		compatible = "nvidia,tegra210-spi";
		reg = <0x0 0x7000d400 0x0 0x200>;
		interrupts = <0x0 0x3b 0x4>;
		iommus = <0x30 0xe>;
		#address-cells = <0x1>;
		#size-cells = <0x0>;
		dmas = <0x51 0xf 0x51 0xf>;
		dma-names = "rx", "tx";
		nvidia,clk-parents = "pll_p", "clk_m";
		clocks = <0x26 0x29 0x26 0xf3 0x26 0xe9>;
		clock-names = "spi", "pll_p", "clk_m";
		resets = <0x26 0x29>;
		reset-names = "spi";
		status = "okay";
		linux,phandle = <0xf8>;
		phandle = <0xf8>;

		prod-settings {
			#prod-cells = <0x3>;

			prod {
				prod = <0x4 0xfff 0x0>;
			};

			prod_c_flash {
				status = "disabled";
				prod = <0x4 0x3f 0x7>;
			};

			prod_c_loop {
				status = "disabled";
				prod = <0x4 0xfff 0x44b>;
			};
		};

		ads1299@0 {
			compatible = "ads1299";
			reg = <0x0>;
			spi-max-frequency = <0x1f78a40>;

			controller-data {
				nvidia,polling-mode;
				nvidia,disable-runtime-pm;
				nvidia,enable-hw-based-cs;
				nvidia,cs-setup-clk-count = <0>;
				nvidia,cs-hold-clk-count = <0>;
				nvidia,rx-clk-tap-delay = <0>;
				nvidia,tx-clk-tap-delay = <0>;
			};
		};

It seems not change the spi transfer ability.

@Gaosiy
Here is response from NV internal.

  1. Can I reduce the time cost when I just use the system api (spi_sync, spi_async).
    Yes the time cost can be reduced.
  2. How the SPI work with DMA? Can DMA control the SPI transfer directly?That is to say, the interrupt trigger the DMA, DMA read data use SPI, and when the data is enough, DMA trigger a callback to let CPU handle the data. If I want realize the idea, should I change the NVIDIA spi controller driver?
    SPI DMA gets triggered when the BS is greater than 256B and it will control the transfer. If you wish to use PIO mode you need to limit to BS of 256B. If your planning to use just the PIO I can provide you a change which would disable the DMA and just run the transfers using PIO.

Thanks, Shane.

  1. How can I reduce the time cost, I add the items you give it seems not to work.
  2. For me, SPI transfer 27 Byte once, maybe I can try your method.

Best wishes!

Hi Shane, any suggestion about PIO?

@ShaneCCC
Hi Shane, do you have any method to disable the DMA?

Hi Shane, any progress about the PIO?

@Gaosiy
Disable the DMA by remove the dmas dma-names in the device tree.

        spi@c260000 {
                compatible = "nvidia,tegra186-spi";
                reg = <0x0 0xc260000 0x0 0x10000>;
                interrupts = <0x0 0x25 0x4>;
                #address-cells = <0x1>;
                #size-cells = <0x0>;
                iommus = <0x2 0x20>;
                dma-coherent;
                dmas = <0x1e 0x10 0x1e 0x10>;
                dma-names = "rx", "tx";

Hi Shane,
When I remove the dmas and dma-names, spi device can not be find in /dev/

Did you load the spidev driver after booted?

sudo modprobe spidev

Yes, I add spidev into

/etc/modules-load.d/modules.conf

so when boot, it will load spidev.

if I re-add the dma item, spidev appear again, so I think maybe the change about DMA has some problem

This is my device tree

	spi@7000d400 {
		compatible = "nvidia,tegra210-spi";
		reg = <0x0 0x7000d400 0x0 0x200>;
		interrupts = <0x0 0x3b 0x4>;
		iommus = <0x30 0xe>;
		#address-cells = <0x1>;
		#size-cells = <0x0>;
		#dmas = <0x51 0xf 0x51 0xf>;
		#dma-names = "rx", "tx";
		nvidia,clk-parents = "pll_p", "clk_m";
		clocks = <0x26 0x29 0x26 0xf3 0x26 0xe9>;
		clock-names = "spi", "pll_p", "clk_m";
		resets = <0x26 0x29>;
		reset-names = "spi";
		status = "okay";
		linux,phandle = <0xf8>;
		phandle = <0xf8>;

		prod-settings {
			#prod-cells = <0x3>;

			prod {
				prod = <0x4 0xfff 0x0>;
			};

			prod_c_flash {
				status = "disabled";
				prod = <0x4 0x3f 0x7>;
			};

			prod_c_loop {
				status = "disabled";
				prod = <0x4 0xfff 0x44b>;
			};
		};

		ads1299@0 {
			compatible = "ads1299";
			reg = <0x0>;
			spi-max-frequency = <0x1f78a40>;

			controller-data {

				nvidia,enable-hw-based-cs;
				nvidia,cs-setup-clk-count = <0>;
				nvidia,cs-hold-clk-count = <0>;
				nvidia,rx-clk-tap-delay = <0>;
				nvidia,tx-clk-tap-delay = <0>;

			};
		};

		spi@1 {
			compatible = "tegra-spidev";
			reg = <0x1>;
			spi-max-frequency = <0x1f78a40>;

			controller-data {
				nvidia,enable-hw-based-cs;
				nvidia,rx-clk-tap-delay = <0x7>;
			};
		};
	};

Could you modify below define in the …/kernel/kernel-4.9/drivers/spi/spi-tegra114.c

#define SPI_FIFO_DEPTH				64

Yes, I can do it, should I change and build a new kernel and flash it?

And I find in the kernel source, the item is 64 already

Yes, it’s 64 already. What I mean is reduce the value.
And also print message to confirm transfer by CPU instead of DMA.

You can build the kernel Image and replace it in Xavier /boot/Image

Sorry Shane, Maybe I misunderstand what you say.
When I delete the DMA items of spi in the device tree. I can not load my spi device driver.

Can I solve this problem by reduce the SPI_FIFO_DEPTH?

Sorry to confuse it. Please revert the DTS modification to modify the kernel driver only.

Thank you, Shane, I get it, so just modify the kernel driver, as you say, reduce SPI_FIFO_DEPTH, this behavior will realize use PIO instead of DMA? If so, I will try it.