Is there any DMA Support for I2C in Xavier NX?

I’m trying to communicate with an I2C Slave via DMA in order to reduce the CPU Utilisation. Right now I’m able to get data to Xavier NX from the Slave Device using i2c_master_send i2c_master_recv on request.

Please note that I’m using Jetpack 4.4.1 (Kernel 4.9.140).

Now I wanted to get these data to Xavier NX through DMA from the I2C Bus. Is there any DMA Support for I2C in Xavier NX. If any possible references are there, please help me to find it out.

hello sebin.thankachan,

may I know what’s the real use-case you’re now running,
please share your current CPU usage, and what’s the expectation.
thanks

Hi @JerryChang

I wanted to transfer nearly 1184 bytes in every 50ms from an MCU connected to the Xavier NX through I2C Bus. Since other CPU consuming processes are also running in background, we need to reduce the latency, CPU usage and the overall traffic. That’s why we wanted to use DMA for this Transfer.

hello sebin.thankachan,

there’s kernel dmatest module can be used for DMA memcpy.
you may visit jetson-linux-r3541 page to download [Driver Package (BSP) Sources] for reference,
for example, $public_sources/kernel_src/kernel/kernel-5.10/drivers/dma/dmatest.c

@JerryChang I tried the example you mentioned(dmatest.c) as per the documentation available online example: DMA Test Guide — The Linux Kernel documentation

But that doesn’t gives me the proper results as per the above requirement. Is there any other resources available or any sample driver available in the kernel sources which i can refer?

may I have more details, for example, what’s your test results.

Hi @JerryChang

Thank you for your response.

My actual requirement is to build it for Jetpack 4.4.1. I need to read out data from an I2C Slave Device connected to i2c-2 of Xavier NX through DMA.

Since you mentioned to use dmatest.c present in the sources of Jetpack 5.1.2(kernel 5.10) for reference, I tried to get the data with kernel 5.10. These are the steps which i followed.

  1. Edited File the below file. ‘Linux_for_Tegra/source/public/hardware/nvidia/soc/t19x/kernel-dts/tegra194-soc/tegra194-soc-i2c.dtsi’
cam_i2c: i2c@3180000 {
		#address-cells = <1>;
		#size-cells = <0>;
		iommus = <&smmu TEGRA_SID_GPCDMA_0>;
		dma-coherent;
		compatible = "nvidia,tegra194-i2c";
		reg = <0x0 0x3180000 0x0 0x100>;
		nvidia,hw-instance-id = <0x2>;
		interrupts = <0 TEGRA194_IRQ_I2C3 0x04>;
		scl-gpio = <&tegra_main_gpio TEGRA194_MAIN_GPIO(P, 2) 0>;
		sda-gpio = <&tegra_main_gpio TEGRA194_MAIN_GPIO(P, 3) 0>;
-		*status = "disabled";*
+		*status = "okay";*
		clock-frequency = <400000>;
		clocks = <&bpmp_clks TEGRA194_CLK_I2C3
			&bpmp_clks TEGRA194_CLK_PLLP_OUT0>;
		clock-names = "div-clk", "parent";
		resets = <&bpmp_resets TEGRA194_RESET_I2C3>;
		reset-names = "i2c";
		dmas = <&gpcdma 23>, <&gpcdma 23>;
		dma-names = "rx", "tx";
	};
  1. Compiled the dmatest.c as built in kernel.
  2. Started the I2C Transactions with the normal I2C Driver and followed the below process, as mentioned in DMA Test Guide — The Linux Kernel documentation
echo 2000 > /sys/module/dmatest/parameters/timeout
echo 1 > /sys/module/dmatest/parameters/iterations
echo dma0chan23 > /sys/module/dmatest/parameters/channel
echo 1 > /sys/module/dmatest/parameters/run

Meanwhile, I was gettings kernel logs like this.
[ 175.356701] dmatest: dma0chan23-copy: summary 1 tests, 0 failures 4329.00 iops 43290 KB/s (0)

  1. Tried to check the bytes transferred as follows.
cat /sys/class/dma/dma0chan23/bytes_transferred
0
cat /sys/class/dma/dma0chan23/memcpy_count
0

So I have below questions,

  1. Is there anything wrong in the process which I’m doing for dmatest.c? If any, can you share the exact resource which I can use?
  2. Should I need to modify anything in the existing driver which I’m using having i2c_master_send and i2c_master_recv APIs in order to DMA support?. If any helpful driver sources are there, please share that one.
  3. Does DMA support for I2C is available in Jetpack 4.4.1(kernel 4.9.140), since that’s my final requirement?

Could you help to resolve this issue.

@JerryChang Can you help me to find a solution for this?

hello sebin.thankachan,

you may visit inux-tegra-r3244 for JetPack-4.4.1 release version.
please also check kernel documentation for the steps for testing DMA drivers using dmatest module.
for example, $public/kernel_src/kernel/kernel-4.9/Documentation/dmaengine/dmatest.txt

BTW,
is this i2c device a camera sensor? if yes, you may try using v4l standard control to access to the device directly.
here’s developer guide for your reference, Applications Using V4L2 IOCTL Directly.

Hi @JerryChang

When we tried to implement it in kernel-4.9, we followed the same procedure only
$public/kernel_src/kernel/kernel-4.9/Documentation/dmaengine/dmatest.txt

We’re trying to read out data from an STM32 MCU through I2C. We’re able to communicate between Xavier NX and MCU using Normal I2C Transactions.

Is there any other references available to read out data from I2C bus using Xavier NX’s GPCDMA?

hello sebin.thankachan,

that’s automatically supported in the driver.
you may refer to below kernel sources, $public_sources/kernel_src/kernel/kernel-4.9/drivers/i2c/busses/i2c-tegra.c

/* Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode,
 * above this, controller will use DMA to fill FIFO.
 * Here MAX PIO len is 20 bytes excluding packet header
 */
#define I2C_PIO_MODE_MAX_LEN                    (32)

...

        if ((tx_len > I2C_PIO_MODE_MAX_LEN || rx_len > I2C_PIO_MODE_MAX_LEN) &&
                        i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan)
                ret = tegra_i2c_start_dma_xfer(i2c_dev, buffer, tx_len, rx_len);
        else
                ret = tegra_i2c_start_pio_xfer(i2c_dev, buffer, tx_len, rx_len);

Hi @JerryChang

Thanks for the quick update. So you’re mentioning that, I need to add take the i2c-tegra.c as the reference in order to add dma support to my existing i2c driver, right?.

as the code snippets, you should increase tx_len and rx_len for using DMA to fill FIFO.

Hi @JerryChang

So you’re mentioning that I don’t need to add any additional DMA support to the existing DMA driver, just increase the tx_len and rx_len. Isn’t?

hello sebin.thankachan,

yes, you may give it a try.
please do also check the description of I2C_PIO_MODE_MAX_LEN for details.
thanks

Hi @JerryChang

Thanks for the response. I had gone through the i2c-tegra.c and varied the I2C_PIO_MODE_MAX_LEN like you mentioned. Since our length is greater than 32 what I observed is that, at everytime the DMAC is called for the operation(start and transfer apis are calling).

#define I2C_PIO_MODE_MAX_LEN			(32)

if ((tx_len > I2C_PIO_MODE_MAX_LEN || rx_len > I2C_PIO_MODE_MAX_LEN) &&
			i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan)
		ret = tegra_i2c_start_dma_xfer(i2c_dev, buffer, tx_len, rx_len);
	else
		ret = tegra_i2c_start_pio_xfer(i2c_dev, buffer, tx_len, rx_len);

	if (ret)
		return ret;

	dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n",
		i2c_readl(i2c_dev, I2C_INT_MASK));

	if ((tx_len > I2C_PIO_MODE_MAX_LEN || rx_len > I2C_PIO_MODE_MAX_LEN) &&
			i2c_dev->tx_dma_chan && i2c_dev->rx_dma_chan) {
		if (i2c_dev->curr_direction & DATA_DMA_DIR_TX) {
			time_left = wait_for_completion_timeout(
					&i2c_dev->tx_dma_complete,
					TEGRA_I2C_TIMEOUT);
			if (time_left == 0) {
				dev_err(i2c_dev->dev,
					"tx dma timeout txlen:%d rxlen:%d\n",
					tx_len, rx_len);
				tegra_i2c_reg_dump(i2c_dev);
				dmaengine_terminate_all(i2c_dev->tx_dma_chan);
				if (i2c_dev->curr_direction & DATA_DMA_DIR_RX)
					dmaengine_terminate_all(
							i2c_dev->rx_dma_chan);
				goto end_xfer;
			}
		}
		if (i2c_dev->curr_direction & DATA_DMA_DIR_RX) {
			time_left = wait_for_completion_timeout(
					&i2c_dev->rx_dma_complete,
					TEGRA_I2C_TIMEOUT);
			if (time_left == 0) {
				dev_err(i2c_dev->dev,
					"rx dma timeout txlen:%d rxlen:%d\n",
					tx_len, rx_len);
				tegra_i2c_reg_dump(i2c_dev);
				dmaengine_terminate_all(i2c_dev->rx_dma_chan);
				goto end_xfer;
			}
			if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) {
				dma_sync_single_for_cpu(i2c_dev->dev,
						i2c_dev->rx_dma_phys,
						i2c_dev->dma_buf_size,
						DMA_FROM_DEVICE);
				memcpy(i2c_dev->rx_pio_buffer,
						i2c_dev->rx_dma_buf,
						rx_len);
				dma_sync_single_for_device(i2c_dev->dev,
						i2c_dev->rx_dma_phys,
						i2c_dev->dma_buf_size,
						DMA_FROM_DEVICE);
			}
		}
	}

So, is there any way to initialise it once with a target buffer and pause/resume the DMAC operation as per the user request?

could you please give more details.
I don’t quite understand your request here…

Hi @JerryChang

We need to read data from MCU connected in the I2C bus in every short duration of time periodically. As of now, for every read cycle the DMA starts and transfers the data. So instead of that is it possible to start DMA once and transfer the data from Slave to specified location of memory periodically, without the/minimal usage of any CPU cycle?

hello sebin.thankachan,

control messages are always with CPU.
there’s no way we can trigger the DMA periodically without CPU involvement.
the DMA channel need to be programmed for every transfer by the CPU.

you may see-also DMA driver for the programming sequence.
for example, $public_sources/kernel_src/kernel/nvidia/drivers/dma/tegra186-gpc-dma.c

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.