Jetson/TX1 SPI

From eLinux.org
Jump to: navigation, search

This is a guide to enabling and testing SPI port functionality on the Jetson TX1 Developer Kit.

The following procedures were verified using JetPack 2.3 / 2.3.1 and are run from the Jetson terminal.

  Note — for updated version for Jetson TX2 / L4T R28.1, see Jetson/TX2_SPI


Configuring GPIO Pinmux for SPI

By default the GPIO pins used for SPI are mapped to an alternate special function, as indicated below under row C (0x1f):

$ sudo cat /sys/kernel/debug/tegra_gpio

Name:Bank:Port CNF OE OUT IN INT_STA INT_ENB INT_LVL
 A: 0:0 24 00 00 24 00 00 000000
 B: 0:1 0f 00 00 00 00 00 000000
 C: 0:2 1f 00 00 00 00 00 000000
 D: 0:3 10 10 00 00 00 00 000000
 E: 1:0 70 00 00 00 00 00 000000
 F: 1:1 00 00 00 00 00 00 000000
 G: 1:2 00 00 00 00 00 00 000000
 H: 1:3 ff 1b 0b 40 00 24 002024
 I: 2:0 0f 0d 01 02 00 00 000000
 J: 2:1 00 00 00 00 00 00 000000
 K: 2:2 f0 20 00 d0 00 80 008080
 L: 2:3 02 00 00 02 00 02 000000
 M: 3:0 00 00 00 00 00 00 000000
 N: 3:1 00 00 00 00 00 00 000000
 O: 3:2 00 00 00 00 00 00 000000
 P: 3:3 00 00 00 00 00 00 000000
 Q: 4:0 00 00 00 00 00 00 000000
 R: 4:1 00 00 00 00 00 00 000000
 S: 4:2 f0 f0 00 00 00 00 000000
 T: 4:3 03 03 00 00 00 00 000000
 U: 5:0 0c 00 00 08 00 00 000000
 V: 5:1 6e 66 00 00 00 00 000000
 W: 5:2 00 00 00 00 00 00 000000
 X: 5:3 ff 00 00 fe 00 70 606000
 Y: 6:0 03 00 00 03 00 01 010100
 Z: 6:1 1f 08 00 17 00 07 030300
AA: 6:2 00 00 00 00 00 00 000000
BB: 6:3 0d 04 00 00 00 00 000000
CC: 7:0 32 30 20 20 00 02 020200
DD: 7:1 00 00 00 00 00 00 000000
EE: 7:2 00 00 00 00 00 00 000000
FF: 7:3 00 00 00 00 00 00 000000

To properly map the GPIO pins to SPI, the following segment must be added to the Device Tree:

+        gpio: gpio@6000d000 {
+                gpio_default: default {
+                       gpio-to-sfio = <16 17 18 19 20>;
+               };

After 28.2 add below to the DT.

   gpio_default: default{
   gpio-hog;
   function;
   gpios = <TEGRA_GPIO(C, 0) 0 TEGRA_GPIO(C, 1) 0 TEGRA_GPIO(C, 2) 0 TEGRA_GPIO(C, 3) TEGRA_GPIO(C,4) 0>;

};

To do that, we will decompile the Jetson TX1's existing Device Tree, add the patch, and recompile the Device Tree.

Installing DTC Tool

Recompiling the kernel is not required for editing/updating the Device Tree and can be done from onboard the Jetson. To do that, first install the Device Tree Compiler (DTC) toolchain package from the Ubuntu aarch64 repository:

 $ sudo apt-get update
 $ sudo apt-get install device-tree-compiler

Locating the Active DTB

Device Tree Binary (DTB) files are located in /boot. The particular DTB loaded during boot is configured in /boot/extlinux/extlinux.conf under the 'FDT' entry:

 $ cat /boot/extlinux/extlinux.conf
 
 TIMEOUT 30
 DEFAULT primary
 
 MENU TITLE p2371-2180 eMMC boot options
 
 LABEL primary
     MENU LABEL primary kernel
     LINUX /boot/Image
     INITRD /boot/initrd
     FDT /boot/tegra210-jetson-tx1-p2597-2180-a01-devkit.dtb
     APPEND fbcon=map:0 console=tty0 console=ttyS0,115200n8 androidboot.modem=none androidboot.serialno=P2180A00P00940c003fd androidboot.security=non-secure tegraid=21.1.2.0.0 ddr_die=2048M@2048M ddr_die=2048M@4096M section=256M memtype=0 vpr_resize usb_port_owner_info=0 lane_owner_info=0 emc_max_dvfs=0 touch_id=0@63 video=tegrafb no_console_suspend=1 debug_uartport=lsport,0 earlyprintk=uart8250-32bit,0x70006000 maxcpus=4 usbcore.old_scheme_first=1 lp0_vec=${lp0_vec} nvdumper_reserved=${nvdumper_reserved} core_edp_mv=1125 core_edp_ma=4000 gpt android.kerneltype=normal androidboot.touch_vendor_id=0 androidboot.touch_panel_id=63 androidboot.touch_feature=0 androidboot.bootreason=pmc:software_reset,pmic:0x0 net.ifnames=0 root=/dev/mmcblk0p1 rw rootwait

This line indicates the file to reference in the following step when the DTB is decompiled to source.

Extracting DTB to Source

Using the DTC tool installed above, extract the DTB back into human-readable Device Tree Source (DTS) file:

$ dtc -I dtb -O dts -o ~/spi_test/extracted.dts /boot/tegra210-jetson-tx1-p2597-2180-a01-devkit.dtb

In this example, the intermediate files are stored in a user-defined folder that was created (in this case /home/ubuntu/spi_test) Look through the extracted DTS text to verify the translation is complete.

Patching the New DTS

Open the extracted DTS in text editor and locate the gpio@6000d000 entry (line ~2762), and append the following snippet to the entry:

 gpio_default: default {
      gpio-to-sfio = <16 17 18 19 20>;
 };

The gpio@6000d000 entry should now reflect the following:

gpio@6000d000 {
		compatible = "nvidia,tegra210-gpio", "nvidia,tegra124-gpio", "nvidia,tegra30-gpio";
		reg = <0x0 0x6000d000 0x0 0x1000>;
		interrupts = <0x0 0x20 0x4 0x0 0x21 0x4 0x0 0x22 0x4 0x0 0x23 0x4 0x0 0x37 0x4 0x0 0x57 0x4 0x0 0x59 0x4 0x0 0x7d 0x4>;
		#gpio-cells = <0x2>;
		gpio-controller;
		#interrupt-cells = <0x2>;
		interrupt-controller;
		gpio-ranges = <0x50 0x0 0x0 0xf6>;
		status = "okay";
		gpio-init-1 = <0x51>;
		linux,phandle = <0x6a>;
		phandle = <0x6a>; 

		wlan-default {
			gpio-input = <0x3a 0x41>;
			gpio-output-high = <0x38 0x40>;
			linux,phandle = <0x51>;
			phandle = <0x51>;
		};

		camera_control {
			gpio-output-low = <0x97>;
			gpio-output-high = <0x94>;
		};

		camera-control {
			gpio-output-low = <0x94 0x97 0x95 0x98>;
		};

		gpio_default: default {
			gpio-to-sfio = <16 17 18 19 20>;
		};
};

We saved this edited file as ~/spi_test/modified-gpio.dts

Recompiling the New DTB

Use the DTC tool again the generate the new DTB from the updated DTS:

 $ dtc -I dts -O dtb -o ~/spi_test/modified-gpio.dtb ~/spi_test/modified-gpio.dts 

Booting with the New DTB

From the 28.2 please update the DTB by replace the DTB file at ../Linux_for_tegra/kernel/dtb/ and update it by below command instead of modify the extlinux.conf

 sudo ./flash.sh -r -k DTB jetson-tx1 mmcblk0p1

Although we have compiled the new DTB, the system is still configured to boot from the old DTB.

First copy the new DTB into /boot, in this example using a naming convention consistent with the existing DTB's with the addition of a -gpio suffix to indicate our changes:

 $ sudo cp /home/ubuntu/spi_test/modified-gpio.dtb /boot/tegra210-jetson-tx1-p2597-2180-a01-devkit-gpio.dtb

Then update /boot/extlinux/extlinux.conf in text editor to reflect using the new DTB. The updated extlinux.conf should now look like so:

$ cat /boot/extlinux/extlinux.conf

TIMEOUT 30
DEFAULT primary 

MENU TITLE p2371-2180 eMMC boot options
 
LABEL primary
      MENU LABEL primary kernel
      LINUX /boot/Image
      INITRD /boot/initrd
      FDT /boot/tegra210-jetson-tx1-p2597-2180-a01-devkit-gpio.dtb
      APPEND fbcon=map:0 console=tty0 console=ttyS0,115200n8 androidboot.modem=none androidboot.serialno=P2180A00P00940c003fd androidboot.security=non-secure tegraid=21.1.2.0.0 ddr_die=2048M@2048M ddr_die=2048M@4096M section=256M memtype=0 vpr_resize usb_port_owner_info=0 lane_owner_info=0 emc_max_dvfs=0 touch_id=0@63 video=tegrafb no_console_suspend=1 debug_uartport=lsport,0 earlyprintk=uart8250-32bit,0x70006000 maxcpus=4 usbcore.old_scheme_first=1 lp0_vec=${lp0_vec} nvdumper_reserved=${nvdumper_reserved} core_edp_mv=1125 core_edp_ma=4000 gpt android.kerneltype=normal androidboot.touch_vendor_id=0 androidboot.touch_panel_id=63 androidboot.touch_feature=0 androidboot.bootreason=pmc:software_reset,pmic:0x0 net.ifnames=0 root=/dev/mmcblk0p1 rw rootwait

Then reboot the system to have the changes take effect.

$ sudo reboot

Verifying GPIO PinMux

After rebooting, check tegra_gpio again, and now row C should be changed to 0x00, verifying those GPIO pins are now mapped to SPI mode:

$ sudo cat /sys/kernel/debug/tegra_gpio

Name:Bank:Port CNF OE OUT IN INT_STA INT_ENB INT_LVL
 A: 0:0 24 00 00 24 00 00 000000
 B: 0:1 0f 00 00 00 00 00 000000
 C: 0:2 00 00 00 00 00 00 000000
 D: 0:3 10 10 00 00 00 00 000000
 E: 1:0 70 00 00 00 00 00 000000
 F: 1:1 00 00 00 00 00 00 000000
 G: 1:2 00 00 00 00 00 00 000000
 H: 1:3 ff 1b 0b 40 00 24 002024
 I: 2:0 0f 0d 01 02 00 00 000000
 J: 2:1 00 00 00 00 00 00 000000
 K: 2:2 f0 20 00 d0 00 80 008080
 L: 2:3 02 00 00 02 00 02 000000
 M: 3:0 00 00 00 00 00 00 000000
 N: 3:1 00 00 00 00 00 00 000000
 O: 3:2 00 00 00 00 00 00 000000
 P: 3:3 00 00 00 00 00 00 000000
 Q: 4:0 00 00 00 00 00 00 000000
 R: 4:1 00 00 00 00 00 00 000000
 S: 4:2 f0 f0 00 00 00 00 000000
 T: 4:3 03 03 00 00 00 00 000000
 U: 5:0 0c 00 00 08 00 00 000000
 V: 5:1 6e 66 00 00 00 00 000000
 W: 5:2 00 00 00 00 00 00 000000
 X: 5:3 ff 00 00 fe 00 70 606000
 Y: 6:0 03 00 00 03 00 01 010100
 Z: 6:1 1f 08 00 17 00 07 030300
AA: 6:2 00 00 00 00 00 00 000000
BB: 6:3 0d 04 00 00 00 00 000000
CC: 7:0 32 30 20 20 00 02 020200
DD: 7:1 00 00 00 00 00 00 000000
EE: 7:2 00 00 00 00 00 00 000000
FF: 7:3 00 00 00 00 00 00 000000

The SPI ports can now be used from the kernel. To test them from userspace, we will install spidev and a loopback test.

Installing SPIdev Kernel Module

The Linux [[1]] driver provides a user interface for accessing SPI to userspace processes and applications. Although SPIdev is not compiled into the L4T kernel by default, it can be compiled and loaded as a kernel module. This is able to be done from onboard the Jetson without recompiling the entire kernel or can be cross-compiled from x86 host if desired.

Downloading Kernel Sources

The source code for SPIdev module and the loopback tests are included in the L4T kernel source code. The following will download and extract the tree used to build the kernel module:

$ wget --no-check-certificate 'https://developer.nvidia.com/embedded/dlc/l4t-sources-24-2-1' -O sources_r24.2.1.tbz2

$ tar -xvf sources_r24.2.1.tbz2
$ cd sources

$ tar -xvf kernel_src.tbz2
$ cd kernel

Updating the Kernel Config

First, get a copy of the Jetson's existing kconfig to start from:

$ zcat /proc/config.gz > .config

Next, enable building SPIdev as module, and set the kernel LOCALVERSION:

$ sed -i 's/# CONFIG_SPI_SPIDEV is not set/CONFIG_SPI_SPIDEV=m/' .config
$ sed -i 's/CONFIG_LOCALVERSION=""/CONFIG_LOCALVERSION="-tegra"/' .config

Building the SPIdev Module

Run these commands to configure the source tree and build the SPI modules, without having to compile the kernel:

$ make prepare
$ make modules_prepare
$ make M=drivers/spi/

Installing the SPIdev Module

Copy the built .ko object into /lib/modules and reboot:

$ sudo cp drivers/spi/spidev.ko /lib/modules/$(uname -r)/kernel/drivers
$ sudo depmod
$ sudo reboot


Enabling SPIdev in the Device Tree

Although the spidev kernel module may be loaded and device node created, the associated SPI channel(s) have not yet been mapped to use spidev driver in the Device Tree.

To bind a Tegra SPI channel to the spidev0.0 node, append the following block to the particular SPI port in the DTS (i.e. spi@7000d400)

spi0_0 {
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    compatible = "spidev";
    reg = <0x0>;
    spi-max-frequency = <20000000>;
    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>;
};

As Configured for the Devkit

The SPI1 channel that is accessible from pins 19 & 21 of the J21 Expansion header on the Jetson TX1 Developer Kit corresponds to device spi@7000d400 in the Device Tree. This is what the updated section of the DTS looks like:

 spi@7000d400 {
		compatible = "nvidia,tegra210-spi";
		reg = <0x0 0x7000d400 0x0 0x200>;
		interrupts = <0x0 0x3b 0x4>;
		nvidia,dma-request-selector = <0x5d 0xf>;
		iommus = <0x46 0xe>;
		#address-cells = <0x1>;
		#size-cells = <0x0>;
		dmas = <0x5d 0xf 0x5d 0xf>;
		dma-names = "rx", "tx";
		nvidia,clk-parents = "pll_p", "clk_m";
		status = "okay";

		prod-settings {

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

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

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

		spi0_0 {
		    #address-cells = <0x1>;
		    #size-cells = <0x0>;
		    compatible = "spidev";
		    reg = <0x0>;
		    spi-max-frequency = <20000000>;
		    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>;
		};
 };

We edited the patched DTS which already had the gpio-to-sfio directives included from before, and added the spi0_0 block to spi@7000d400 above. We saved the new DTS file as ~/spi_test/modified-gpio-spi0.dts. Next, recompile the DTB again, now with spidev enabled.

Compiling the DTB for SPIdev

As before, build the DTB again, now with spidev enabled on J21 Expansion header for the devkit. As per the previous section, we're calling this one modified-gpio-spi0.dtb

$ dtc -I dts -O dtb -o ~/spi_test/modified-gpio-spi0.dtb ~/spi_test/modified-gpio-spi0.dts 
$ sudo cp ~/spi_test/modified-gpio-spi0.dtb /boot/tegra210-jetson-tx1-p2597-2180-a01-devkit-gpio-spi0.dtb

Booting the DTB with SPIdev

As before, extlinux.conf needs changed over to load the new DTB at boot. The updated extlinux.conf should now look like:

$ cat /boot/extlinux/extlinux.conf

TIMEOUT 30
DEFAULT primary 

MENU TITLE p2371-2180 eMMC boot options
 
LABEL primary
      MENU LABEL primary kernel
      LINUX /boot/Image
      INITRD /boot/initrd
      FDT /boot/tegra210-jetson-tx1-p2597-2180-a01-devkit-gpio-spi0.dtb
      APPEND fbcon=map:0 console=tty0 console=ttyS0,115200n8 androidboot.modem=none androidboot.serialno=P2180A00P00940c003fd androidboot.security=non-secure tegraid=21.1.2.0.0 ddr_die=2048M@2048M ddr_die=2048M@4096M section=256M memtype=0 vpr_resize usb_port_owner_info=0 lane_owner_info=0 emc_max_dvfs=0 touch_id=0@63 video=tegrafb no_console_suspend=1 debug_uartport=lsport,0 earlyprintk=uart8250-32bit,0x70006000 maxcpus=4 usbcore.old_scheme_first=1 lp0_vec=${lp0_vec} nvdumper_reserved=${nvdumper_reserved} core_edp_mv=1125 core_edp_ma=4000 gpt android.kerneltype=normal androidboot.touch_vendor_id=0 androidboot.touch_panel_id=63 androidboot.touch_feature=0 androidboot.bootreason=pmc:software_reset,pmic:0x0 net.ifnames=0 root=/dev/mmcblk0p1 rw rootwait

This time, shut down the system so we can install the loopback jumper. Upon start-up, the DTB changes should have taken effect.

$ sudo shutdown -a now

Verifying SPIdev has Loaded

After rebooting, the kernel should automatically have loaded spidev.ko.

You can check this by listing the currently loaded modules with lsmod:

$ lsmod

Module                  Size  Used by
 bnep                   14896  2
 bcmdhd               7465032  0
 cfg80211              452899  1 bcmdhd
 spidev                 8679  0 
 bluedroid_pm           11420  0

There should also be a device node created under /dev called /dev/spidev0.0

$ ls /dev/spi*
     /dev/spidev0.0 


Loopback Testing

A simple loopback test is included in the kernel source. After the SPI pins MOSI/MISO are shorted via jumper or a short wire, the tool verifies that MOSI/MISO values are transmitted/received the same. This is useful for verifying data integrity of the Jetson setup before connecting 3rd-party SPI peripherals.

Installing Loopback Wire

The Jetson TX1 Developer Kit includes the SPI1 channel on it's J21 Expansion header. The SPI1 MOSI/MISO signals are pins 19 & 21 on J21.

With the system shutdown, install a jumper or short wire (<10cm) between pins 19 & 21 to complete the circuit for the loopback test.

Compiling the Loopback Test

The loopback test source code is included in the kernel tree used above to the compile SPIdev module. If you go back to where you downloaded and extract the L4T source to, the loopback test is located under sources/kernel/Documentation/spi.

A simple gcc command compiles it:

$ cd sources/kernel/Documentation/spi
$ gcc -o spidev_test spidev_test.c

Running the Loopback Test

The following command will initiate loopback testing using spidev0.0

$ sudo ./spidev_test -D /dev/spidev0.0

spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

FF FF FF FF FF FF 
40 00 00 00 00 95 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
FF FF FF FF FF FF 
DE AD BE EF BA AD 
F0 0D 

The above output indicates that the test completed successfully, and the SPI channel data integrity is confirmed OK.