Jetson/TX1 SPI
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.
Contents
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>; + };
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
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 desiorange.
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
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
Enabling SPIdev in the Device Tree
To bind a Tegra SPI port to the spidev0.0 driver 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
Loopback Test
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.