Custom Carrier Board Pinmux and DTS Process

the logs_plus.zip contains the .dtsi files generated by the excel script, the output of “dmesg” which shows that the correct .dts is being loaded during boot, the output of “cat /sys/kernel/debug/gpio”, the .dtb file generated after replacing the .dtsi files as described in my previous post, and the decompiled .dts file generated from the .dtb using dtc.

Tell me the steps I need to take to use the pinout I have defined in the excel script.

The excel script with the changes that need to be implemented can be obtained here:
NV_Jetson_Nano_Module_Pinmux_Config_Template.xlsm

Jerry,

We have been at this for more than a month, and this should not have taken this long. We really need your support, and as I had asked when I was posting here, we need a “step-by-step” process for this that works.

Perhaps the right answer from you all would be a document that starts with the Excel spreadsheet, then gives us very detailed and not obscure steps to get to the end result. We are capable of performing just about any task from simply using UBOOT to setup the pinmux, to whatever we need to do, including compiling our changes into the kernel, flashing EEPROM, etc.

We have used devicetrees for years on other platforms, across many projects, and not once did this take this monumental effort to get our I/O where we want it.

So, what I am asking is for a “step-by-step” method to start with the output from the Excel sheet, to success. Giving us small trinkets of information is not going to work, start from the beginning and get us to the endgame of having our pins work as we expect.

Thank you in advance.

1 Like

Nvidia team, we really need some help here. This is a significant volume, commercial product which is visible to your management, since our customer is pretty big, and all that I can tell them is that we are waiting on getting the pinmux setup right to check out the final board design before we go to the production version.

This cannot be this difficult to accomplish. As I asked two days ago, we need a step-by-step method/document that starts with the Excel pinout document, and gets us to a mapped pin function every time.

We had a goal of last Friday to release final board changes to the production team, but we missed that…and now the pressure is building. Please take this seriously as our timeline is well behind now, and next steps may be management asking me for “who they can call” to get this accomplished.

Thank you in advance.

I do not know how to turn off “Solution” on this thread, we are not SOLVED.

TomK, we noticed that this thread shows “Solution” checked, and not sure where this is in the main Nano forum, but we are still down and need help, 6 weeks now!

I don’t see a solution checked now. Maybe one of the moderators made the change.

hello james.rhodes,

I’d already share the steps in the previous comments,
how about you list all your steps and please share the failure you’ve seen.
thanks

Please review post #18.

After compiling and implementing the device tree .dtb, we are not able to actually use the pinmux changes within the OS.

When we cat /sys/kernel/debug/gpio the gpio still shows the default configuration.

This is the output from …/debug/gpio: http://git.groesz.org/NED/nanotools/raw/branch/master/debug/What%20is%20There.txt

We aren’t seeing the changes we made in device tree be reflected in the OS/Kernel.

1 Like

hello sgroesz,

the steps looks correct, and it’s DTB to define the pin settings.
may I know what’s the pins you’re try to configure without success?
thanks

The excel script with the changes that need to be implemented can be obtained here:
NV_Jetson_Nano_Module_Pinmux_Config_Template.xlsm

We need to be able to use the pin definitions as seen here:

// Tegra GPIO groups for math below
#define TEGRA_GPIO_BANK_ID_A 0
#define TEGRA_GPIO_BANK_ID_B 1
#define TEGRA_GPIO_BANK_ID_C 2
#define TEGRA_GPIO_BANK_ID_D 3
#define TEGRA_GPIO_BANK_ID_E 4
#define TEGRA_GPIO_BANK_ID_F 5
#define TEGRA_GPIO_BANK_ID_G 6
#define TEGRA_GPIO_BANK_ID_H 7
#define TEGRA_GPIO_BANK_ID_I 8
#define TEGRA_GPIO_BANK_ID_J 9
#define TEGRA_GPIO_BANK_ID_K 10
#define TEGRA_GPIO_BANK_ID_L 11
#define TEGRA_GPIO_BANK_ID_M 12
#define TEGRA_GPIO_BANK_ID_N 13
#define TEGRA_GPIO_BANK_ID_O 14
#define TEGRA_GPIO_BANK_ID_P 15
#define TEGRA_GPIO_BANK_ID_Q 16
#define TEGRA_GPIO_BANK_ID_R 17
#define TEGRA_GPIO_BANK_ID_S 18
#define TEGRA_GPIO_BANK_ID_T 19
#define TEGRA_GPIO_BANK_ID_U 20
#define TEGRA_GPIO_BANK_ID_V 21
#define TEGRA_GPIO_BANK_ID_W 22
#define TEGRA_GPIO_BANK_ID_X 23
#define TEGRA_GPIO_BANK_ID_Y 24
#define TEGRA_GPIO_BANK_ID_Z 25
#define TEGRA_GPIO_BANK_ID_AA 26
#define TEGRA_GPIO_BANK_ID_BB 27
#define TEGRA_GPIO_BANK_ID_CC 28
#define TEGRA_GPIO_BANK_ID_DD 29
#define TEGRA_GPIO_BANK_ID_EE 30
#define TEGRA_GPIO_BANK_ID_FF 31
#define TEGRA_GPIO(bank, offset) ((bank * 8) + offset)
//
// Reset to the safety board, output
#define RESET_OUT TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 3) // SODIMM pin 209, GPIO3_PG.00
#define RESET_OUT_DIRECTION e_OUTPUT
// SW1
#define SW1_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 4) // SODIMM pin 104, GPIO3_PB.04
#define SW1_IN_DIRECTION e_INPUT
#define SW1_IN_LEVEL e_BOTH
// SW2
#define SW2_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 6) // SODIMM pin 106, GPIO3_PB.06
#define SW2_IN_DIRECTION e_INPUT
#define SW2_IN_LEVEL e_BOTH
// VOLUP
#define VOLUP_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 5) // SODIMM pin 108, GPIO3_PB.05
#define VOLUP_IN_DIRECTION e_INPUT
#define VOLUP_IN_LEVEL e_BOTH
// VOLDN
#define VOLDN_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 7) // SODIMM pin 110, GPIO3_PB.07
#define VOLDN_IN_DIRECTION e_INPUT
#define VOLDN_IN_LEVEL e_BOTH
// ID0
#define ID0_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 0) // SODIMM pin 203, GPIO3_PG.00
#define ID0_IN_DIRECTION e_INPUT
// ID1
#define ID1_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 1) // SODIMM pin 205, GPIO3_PG.01
#define ID1_IN_DIRECTION e_INPUT
// ID2
#define ID2_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 2) // SODIMM pin 207, GPIO3_PG.02
#define ID2_IN_DIRECTION e_INPUT
// Laser Disable
#define LASDIS_OUT TEGRA_GPIO(TEGRA_GPIO_BANK_ID_V, 0) // SODIMM pin 206, GPIO3_PV.00
#define LASDIS_OUT_DIRECTION e_OUTPUT
// NANOSW
#define NANOSW_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_Z, 0) // SODIMM pin 216, GPIO3_PZ.00
#define NANOSW_IN_DIRECTION e_INPUT
// Speaker Enable
#define SPKREN_OUT TEGRA_GPIO(TEGRA_GPIO_BANK_ID_Y, 2) // SODIMM pin 218, GPIO3_PY.02
#define SPKREN_OUT_DIRECTION e_OUTPUT
// PWRSW
#define PWRSW_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_X, 5) // SODIMM pin 240, GPIO3_PX.05
#define PWRSW_IN_DIRECTION e_INPUT

// A LOT OF UNUSED GPUO!!
// GPIO @ pin 89 NOT USED
#define GPIO_PIN89 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_C, 0) // SODIMM pin 89, GPIO3_PC.00
#define GPIO_PIN89_DIRECTION e_INPUT
// GPIO @ pin 91 NOT USED
#define GPIO_PIN91 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_C, 2) // SODIMM pin 91, GPIO3_PC.02
#define GPIO_PIN91_DIRECTION e_INPUT
// GPIO @ pin 93 NOT USED
#define GPIO_PIN93 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_C, 1) // SODIMM pin 93, GPIO3_PC.01
#define GPIO_PIN93_DIRECTION e_INPUT
// GPIO @ pin 95 NOT USED
#define GPIO_PIN95 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_C, 3) // SODIMM pin 95, GPIO3_PC.03
#define GPIO_PIN95_DIRECTION e_INPUT
// GPIO @ pin 97 NOT USED
#define GPIO_PIN97 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_C, 4) // SODIMM pin 97, GPIO3_PC.04
#define GPIO_PIN97_DIRECTION e_INPUT
// GPIO @ pin 88 NOT USED
#define GPIO_PIN88 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_C, 6) // SODIMM pin 88, GPIO3_PC.06
#define GPIO_PIN88_DIRECTION e_INPUT
// GPIO @ pin 118 NOT USED
#define GPIO_PIN118 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_S, 5) // SODIMM pin 118, GPIO3_PS.05
#define GPIO_PIN118_DIRECTION e_INPUT
// GPIO @ pin 195 NOT USED
#define GPIO_PIN195 TEGRA_GPIO(TEGRA_GPIO_BANK_ID_J, 5) // SODIMM pin 195, GPIO3_PJ.05
#define GPIO_PIN195_DIRECTION e_INPUT

hello sgroesz,

here’s re-definition that might got confused.
for example,
$L4T_Sources/r32.5.1/Linux_for_Tegra/source/public/kernel/kernel-4.9/include/dt-bindings/gpio/tegra-gpio.h

48  #define TEGRA_GPIO(port, offset) \
49  	((TEGRA_GPIO_PORT_##port * 8) + offset)

there’s marco for TEGRA_GPIO(), and the pin, GPIO3_PG.00 should be defined as TEGRA_GPIO(G, 0) in the code.
please have a try,
thanks

Jerry,

Not sure what you are telling us is not correct. From the prior post, we are using a macro that is the same as what you posted,
#define TEGRA_GPIO(bank, offset) ((bank * 8) + offset)

//
#define TEGRA_GPIO_BANK_ID_G 6
// Reset to the safety board, output
#define RESET_OUT TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 3) // SODIMM pin 209, GPIO3_PG.03
#define RESET_OUT_DIRECTION e_OUTPUT

would then resolve to (6*8)+3, which is gpio51 and we believe mapped to pin 209 on the SODIMM.

what is not correct there in how we implemented this? Is the mapping for this GPIO on pin 209 correct in how we resolve the mapping?

Hopefully this helps a little more, with more data…

output from: ls /sys/class/gpio
gpio12
gpio13
gpio14
gpio15
gpio168
gpio200
gpio48
gpio49
gpio50
gpiochip0
gpiochip504

These are my GPIO instantiators…
Reset_Out->enableGPIO
SW1_In->enableGPIO
SW2_In->enableGPIO
VOLUP_In->enableGPIO
VOLDN_In->enableGPIO
ID0_In->enableGPIO
ID1_In->enableGPIO
ID2_In->enableGPIO
LASDIS_Out->enableGPIO
NANOSW_In->enableGPIO

Here are the macro definitions for the pins
#define RESET_OUT TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 3) // SODIMM pin 209, GPIO3_PG.03
#define SW1_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 4) // SODIMM pin 104, GPIO3_PB.04
#define SW2_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 6) // SODIMM pin 106, GPIO3_PB.06
#define VOLUP_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 5) // SODIMM pin 108, GPIO3_PB.05
#define VOLDN_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_B, 7) // SODIMM pin 110, GPIO3_PB.07
#define ID0_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 0) // SODIMM pin 203, GPIO3_PG.00
#define ID1_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 1) // SODIMM pin 205, GPIO3_PG.01
#define ID2_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_G, 2) // SODIMM pin 207, GPIO3_PG.02
#define LASDIS_OUT TEGRA_GPIO(TEGRA_GPIO_BANK_ID_V, 0) // SODIMM pin 206, GPIO3_PV.00
#define NANOSW_IN TEGRA_GPIO(TEGRA_GPIO_BANK_ID_Z, 0) // SODIMM pin 216, GPIO3_PZ.00

I do not see the correlation other than there are 9 IO pins defined there, so perhaps this is working as far as gpio export goes, but then not sure if the pinmux lets the pin actually be assigned to the gpio.

hello james.rhodes,

we’ve checked, pin-209, GPIO3_PG.03, it’s pinmux sheet allows to configure it as GPIO.
may I know which pin you’re NOT able to configure as GPIO? thanks

GPIOs seem to be fine now.

hello james.rhodes,

so, we’re able to close this topic?
could you please have summarization to help others to read this easily since this is quite a long discussion thread.
thanks

This topic can be closed. I will try to summarize what describe my journey which ended up working for us.

Firstly, to recap the original issue, the documentation at times seemed to be unclear, incomplete or contradictory. I mostly attribute this to the users lack of familiarity with the platform, documentation, and source code.

With that said, once we became frustrated due to both the documentation and our unfamiliarity with the process, we simplified the need to:
Problems:

  1. The Jetson Nano SoM pin configuration needs to be modified to provide different functions according to the custom carrier board used in the final product.
  2. How to verify the correct pinmux configuration is in place.

The preferred solution would lay out clearly and directly the steps necessary to solve the problem. That is, something similar to:

  1. Download the pinmux spreadsheet
  2. Generate the .dtsi and .dtsi files from the spreadsheet
  3. Download the kernel source code and the Linaro toolchain
  4. Replace files x.dtsi and y.dtsi with the files generated by the pinmux spreadsheet
  5. run [command to generate new .dtb file]
  6. Copy the new .dtb file to [Linux_for_Tegra]/…
  7. Execute “build_l4t_bup.sh” to generate a Bootloader Update Payload file
  8. copy the Bootloader Update Payload to the target Nano system and run /usr/sbin/l4t_payload_updater_t210 “${BUPFILE}”
  9. Verify SoM functionality by doing [A B C]

I’m still not entirely sure the best way to verify the configuration of each individual pin of the SoM. The best I can do currently is to run “dmesg | grep -i dtb” on the target system and verify that reported date matches the date of my updated .dtb file. ex:

$ dmesg | grep -i dtb
[    0.212463] DTB Build time: Jul 15 2021 12:01:29
[    0.424809] DTB Build time: Jul 15 2021 12:01:29

However, dmesg doesn’t tell me the specific configuration of an individual pin. As such, when the software developer attempts to write code to utilize the new hardware settings and it doesn’t work as expected, it’s difficult to pinpoint whether the problem is with the hardware configuration or the application code. If the developer expected pin 123 to be configured a certain way, how can we prove the hardware is configured properly? At some point, we found

$ cat /sys/kernel/debug/gpio

Which we expected to tell use the current state of the hardware. However, all attempts to update the device tree never changed the output of this command. Eventually, not finding clear answers, I resorted to brute force by analyzing the binary code of the SD card image, where I found MANY copies of the device tree binary. However, this still was not the solution, as ultimately, it was a 4MiB SPI flash chip embedded on the SoM that is responsible for loading the devicetree binary that actually gets loaded. Information regarding this SPI flash chip is VERY sparse in the documentation - in fact, in the official docs, I only recall seeing it mentioned once or twice, and the mentions didn’t clearly tie it to the functioning of the device tree. I read somewhere that bootloading from the 4MiB flash was a recent change, ie it changed in L4T Rev 32.5 - but I don’t recall where I read that at this point.

Once it was clear that finding the answer in the docs was not getting us where we needed to be, we searched the internet at large, where, through various articles and forum posts, the conventional wisdom appeared to me that the devicetree configuration is dictated on the SD card.

Thus, I resorted to brute-force changing every instance of the devicetree binary on the SD card - 9? 11? more??? copies of the binary code were replaced using the linux dd command. And even still, after being able to boot the system and verifying that there was no trace of the original devicetree binary anywhere on the sd card by using a hex editor, the system still booted showing the same “cat /sys/kernel/debug/gpio” and “dmesg | grep -i dtb” dates as the original devicetree.

Finding that the loaded binary data didn’t match every single copy of the binary data on the sd card, by this point the frustration was palpable! I finally solved this myself by running “lsblk” and looking at each block device present on the Nano and searching through the binary data, where I found 3 more copies of the original devicetree binary. My first attempt and blindly (and recklessly) trying to modify this binary data directly with dd bricked my Nano. So, at least I knew I was getting somewhere!

Eventually, after further frustration and a few more weeks of trial and error, I figured out how to at least get “dmesg | grep -i dtb” to show the correct device tree binary date.

After many weeks of working on this, I am now pretty familiar with the documentation, the process, and the source code. It’s difficult to pinpoint the specific issues with the documentation. Had I read the entirety of the documentation from start to finish, I think I could be expected to solve the problem. But reading the entirety of the docs, and comprehending the docs, would take many hours, which is ultimately what I spent to solve the original problem of “how to update the Nano devicetree”.

My solution above is broad and not specific, but it gives a general overview of the final process I am currently using to update the devicetree binary. A lot of this time could have been saved if there were a single, clear guide that specifically addressed the process to take a new Jetson Nano and change the functional hardware pinmux, from step 1 to step n. A single guide such as the one I outline above would go a long way to helping future engineers who embark upon this process.

Apologies for the long post, but it was a long process to get where I am currently from start to finish. With the update to Revision 32.6, as of yesterday, I overhauled my process from brute-force modifying the binary data on the flash memories with dd, to a process that uses the BUP process. From my own initial involvement in May until yesterday, that’s how long this process took. I would estimate no less than 100 hours of direct time working on this. A lot of that could probably be attributed to personal lack of experience and familiarity with the system. Perhaps if we had taken the normal approach to development, that is by using developing the software using the Development kit rather than directly building a custom carrier board and diving in directly to using the custom hardware, the time spent with the dev kit hardware would have given us some insight into how the system works before jumping straight to trying to modify the devicetree. Part of the problem is that the team of engineers working on this project has decades of hardware and software engineering experience, and this process is different from others that the team is already familiar with, such as the BBB or Ras. Pi.

On one hand, I am glad I had to go through this process, as I now know a lot about the internal working of the Nano OS that, if I had been able to solve this by reading a single How-To guide, I would not have learned what I know now. Whether that knowledge is useful…

Currently, I have a single script that, with just 4 files: 2 .sh files and the two .dtsi files from the pinmux spreadsheet, a single, executable file can be created that, when run on a target Nano, will update the functional pinmux reliably. If I had started with this script, and known a way to verify whether the devicetree update process was successful, I could now reliably change the devicetree pinmux within 10 minutes of exporting the .dtsi files from the pinmux spreadsheet. My 100+ hours of labor allowed me to condense the entire process down to a .sh script which automates the entire process of building the devicetree binary.

My current process is:

  1. Download pinmux spreadsheet
  2. Set up pinmux spreadsheet for my target platform and export the .dtsi and .dtsi files
  3. Run my build.sh script, which will manage everything between step 2 and step 4, including downloading necessary packages from Nvidia
  4. copy resultant executable file built by build.sh to a target Nano device
  5. Run the executable produced by build.sh
  6. reboot Nano
  7. verify expected “dmesg | grep -i dtb” date

The entire process from step 3 to step 7 takes about 5 minutes, depending on development hardware, internet speeds, and number of files that need to be downloaded (files are cached so they need only be downloaded the first time).

1 Like

Hi, I am using an emmc SOM and I am trying to disable HDMI through but HDMI still works when cable is connected to the Nano.

Does changing pinmux change default behaviour or does it always disable what I disable?
I don’t know how to verify that pinmux changes are reflected…

I have run dmesg | grep -i dtb and it does reflect the correct date. I have been following this thread for months and I am still unclear about what I need to do.

I can share details of my approach. Also, do let me know if I should start a new thread or continue here

Thank you

dtb_patchbuilder.tgz (3.5 KB)
I’ve attached the script I used to build the update from the dtsi files. Take a look at build.sh to see the steps to build the .dtb files. Particularly the __build_dtb function starting on line 103

In my case, I replaced the 4 files:
cp -v "${GPIO}" tmp/hardware/nvidia/platform/t210/porg/kernel-dts/porg-platforms/tegra210-porg-gpio-p3448-0000-b00.dtsi
cp -v "${GPIO}" tmp/hardware/nvidia/platform/t210/porg/kernel-dts/porg-platforms/tegra210-porg-gpio-p3448-0002-b00.dtsi
cp -v "${PINMUX}" tmp/hardware/nvidia/platform/t210/porg/kernel-dts/porg-platforms/tegra210-porg-pinmux-p3448-0000-b00.dtsi
cp -v "${PINMUX}" tmp/hardware/nvidia/platform/t210/porg/kernel-dts/porg-platforms/tegra210-porg-pinmux-p3448-0002-b00.dtsi

and then when I built the bootloader update package (bup) I made sure to replace all 3 Nano .dtb files.

If the date is showing correctly and the configuration still isn’t correct, it’s possible your changes didn’t actually get loaded into the new dtb (just a guess).

Also, poke around in /proc/device-tree, which is a reflection of the actual device tree configuration in use.

for example:
cat /proc/device-tree/nvidia,dtbbuildtime
is an alternative method to check the date of the actual loaded device tree.
cat /proc/device-tree/nvidia,dtsfilename
will show the file name of the dts file used, in case a different .dtb file other than expected has been loaded.

Lastly, use the following command to export the active devicetree configuration to a text file
dtc -I fs -O dts -o /root/active_devicetree_config.dts /proc/device-tree

Then review the resultant /root/active_devicetree_config.dts and compare some of the changes from your .dtsi files to the contents of the dts file and make sure they are as expected. If something between the .dts file and your .dtsi files doesn’t match, then the .dtsi files were not incorporated into the currently loaded device tree.

If you put your .dtsi files in the same directory as the files in the .tgz I shared above and then run the script, it will create a single .sh file that you can use that should definitively load your changes.

It was tested with the previous version of Tegra, 4.5.1, but it should work with 4.6. Either way, the script will also download the necessary files to complete all the steps, including building the new .dtb and bootloader update, and package it all neatly for you into a single .sh file that can be run on your Nano to push the changes.

Make sure you also pin some deb packages, otherwise if you update them your changes will be overwritten:

apt-mark hold nvidia-l4t-bootloader nvidia-l4t-kernel-dtbs nvidia-l4t-kernel

should work to prevent any unwanted package updates that could unravel your changes, though this is what I’m currently working on and haven’t fully tested this hypothesis yet. That said, before pushing your changes, make sure the system is updated as preferred. Any future updates to those 3 packages will require you to re-run the bupdate.sh, which SHOULD reimplement your changes with the new kernel packages - but also, untested as of right now.

1 Like