How to set Jetson TK1 registers to control SPI bit rate

I am trying to get the touch spi system working so I can use it for relatively high-speed communications with other devices. (In other words for something other than a touch system.) I have figured out how to modify the dts for the jetson to turn on a spidev interface for the spi lines. I can send data at 25 Mhz from that line now. However, no matter how I set the spi-max-frequency for the spi controller, or for the spidev protocol entry in the dts file, I cannot change the bus frequency of the spi. I also wrote a little test app and used ioctrl to set the max bus speed programmatically after opening the spidev port. When I queried it again it said it changed the speed correctly, but in the next line I sent data and it was still at 25 Mhz. It is as if I cannot actually configure the bit rate.

I started reading through the TRM and in section 35.2.4 on SPI clock initialization and control it states “In addition, the clock source and divide registers … contain the 2-bit field … to determine the PLL clock source, while the SBCx_CLK_DIVISOR field determines the divide ratio.” Then in section 5.7.55 it lists CLK_RST_CONTROLLER_CLK_SOURCE_SPI1_0 with bit 7:0 SPI1_CLK_DIVISOR: N = Divide by (n+1).

So it sounds like the spi bit rate is controlled by this divider value in this register. However, I have no idea how to set this register or even check it. I have found some references to using Uboot to examine the registers. I am going to look into trying that, but I was wondering if anyone knows how these registers are set on boot, how I go about finding what they are set to, and how to change them. I have read through the section of the TRM about the register values offsets, but I frankly did not find it very enlightening. I do not see how those register offset values are mapping to something in the actual boot records of my board and where I find them? I was wondering if someone could explain this, or can point me to a link that has a more detailed explanation that I could start digging through. Also, has anyone already done this with the touch SPI and gotten it working? If so then can you share that with me? Does it look like I am even in the ballpark with my hypothesis that this divider register setting is what I need to change to reduce the SPI bit rate?


I haven’t worked with the SPI, but it should be similar to GPIO (which I do work with) for what you’re asking. GPIO is easy to experiment with under u-boot, and the register offset and base address syntax would be the same for SPI. It seems like this and many similar register questions revolve around the difference between physical addresses and virtual addresses.

Under u-boot, you access registers by physical address. These physical addresses are the addresses used in the TRM, and correspond to the physical hardware address lines. Once you boot into the linux kernel, physical address tracking gets complicated fast because of virtual memory. Most addresses used in the kernel (once MMU is set up…which is very early on) no longer refer to physical address, but instead to virtual address which is automatically translated. In order to work with the register you want you will need to find the virtual address which the physical address was mapped to.

So although I’m not really giving much useful information, there’s your job at this point: Find the virtual address to which the physical address has been mapped. Prior to kernel loading, it’s easy because you are working directly with physical address.

Thanks, that does help. I don’t think I stated my question clearly enough though. I will try and inspect the physical addresses using Uboot. If I understand the way the boot process works correctly, then there has to be something like a dts entry, a board specific config file, or a driver initialization that is setting important register values like this during the boot process. It may be that a lot of registers are just using the default value that is stored in the chip or something, but surely there is a way of configuring the boot process to re-define register values? I am unsure how the jetson is doing that. I was hoping there was a file somewhere I could look at that configured that registry setting during the boot process and I could just change it to what I want. I could be way off on this though because I am still learning how linux works. I was hoping someone could clarify how that works and possibly point me to some relevant documentation so I can try and dig into. Thanks for you help.

One thing the boot loader must do is to set up just enough of the registers that hardware required in boot CAN be used. This might mean setting up clocks, enabling/disabling cases where multiple functions are available on a single pin (mutually exclusive), disk controllers, etc. More of this CAN be set up by a boot loader, such as network cards since some people might net boot. Mostly I think the memory management hardware has not been set up at this stage unless there is some special reason…addresses remain physical address because of this. Then the kernel begins to take over.

One of the first things done in kernel initialization is to set up memory management and remap many addresses to virtual. This is probably one of the most complicated parts of boot IMHO. One hybrid boot loader/kernel shared behavior which is worth noting here is that although some devices were probably set up during boot loader, the kernel may or may not overwrite this functionality with its own drivers. Full distributions like L4T probably overwrite most u-boot functions with their own, but there are still register states which probably are preserved, such that when the new driver begins some of that state is probably an exact copy of what started in u-boot. Two examples probably explain the reasons for this importance:

  1. I recall someone losing network connectivity on Jetson after booting to a certain stage, although it was available early on...the parameters changed because of the new driver load (normally not an issue because normally you don't maintain a network connection from boot loader stage to full boot). Solution is to match u-boot driver parameters in the network config on the file system as used by the NIC.
  2. I develop on a different Tegra (still ARMv7 architecture and nVidia GPU) device as well which is older and has fewer resources a result it does not use a "full" linux distribution (size too large for platform), but it does make networking available from u-boot...unfortunately kernel load does not install its own separate new driver, it instead keeps whatever was loaded from the boot loader. All of this networking is for a single network card and installing a second network card is not possible because only the NIC installed and loaded at u-boot ever gets other net driver takes over. Any changes during u-boot also cause the booted system to take on this behavior, reducing total memory requirements and removing flexibility (working on a bridge so multiple NICs were a requirement). This same cut down system also sets parts of video behavior from u-boot and so X11 configuration is completely ignored for driver options after boot, giving the appearance of hardware failure until realizing those values work great if and only if applied from u-boot.

The kernel files you are interested in regarding SPI will have a name and other mappings in kernel source somewhere in “arch/arm/mach-tegra” subdirectory. Exact files I’m not sure of, but it looks like possibly the board-ardbeg* and/or some of the tegra12* files (the Jetson SoC is tegra12x series, specifically tegra124) in that directory. YMMV. Related are the data tree files telling the kernel which hardware is there (for u-boot, located at /boot/*.dtb, plus some kernel files corresponding to this or Jetson’s pm375 board which tegra124 sits on).

FYI: I just posted a blog series that explains in detail how to use the touch screen SPI on the Jetson to send and receive data at 25 MHz. You can find it here: