Unable to see PWM on scope on Xavier AGX


I am using the Seeed Studio’s H01 dev kit for the Xavier AGX.
I am attempting to get PWM output working for one of the three PWM outputs on the 40-pin header (pins 13, 15, 18)

I have been messing around with the sysfs nodes in /sys/class/pwm/, trying to see some PWM output on the scope. So far I have been unable to see anything that resembles a square wave.

My first question is this:
How can I figure out the relationship between the GPIO pin from the pinmux spreadsheet (i.e. GPIO3_PR.00 for pin 13 on the 40-pin header), and the pwmchip sysfs device?

My second question is this:
Do I need to be changing the device tree at all to get PWM to work? looking at the 40-pins with the jetson-io script shows that PWM is already enabled for those pins.

Any help would be greatly appreciated.


For the first question, you can get the mapping between pwmchip and their address with ls -la /sys/class/pwm/, then with the address, you can refer to the device tree source Linux_for_Tegra/source/public/hardware/nvidia/soc/t19x/kernel-dts/tegra194-soc/tegra194-soc-pwm.dtsi to know the relationship between PWM pins and their address.:

17 / {
18 	tegra_pwm1: pwm@3280000 {
19 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
20 		reg = <0x0 0x3280000 0x0 0x10000>;
21 		nvidia,hw-instance-id = <0x0>;
22 		clocks = <&bpmp_clks TEGRA194_CLK_PWM1>,
23 		       <&bpmp_clks TEGRA194_CLK_PLLP_OUT0>,
24 		       <&bpmp_clks TEGRA194_CLK_CLK_M>;
25 		clock-names = "pwm", "parent", "slow-parent";
26 		#pwm-cells = <2>;
27 		resets = <&bpmp_resets TEGRA194_RESET_PWM1>;
28 		reset-names = "pwm";
29 		status = "disabled";
30 	};
32 	tegra_pwm2: pwm@3290000 {
33 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
34 		reg = <0x0 0x3290000 0x0 0x10000>;
35 		nvidia,hw-instance-id = <0x1>;
36 		clocks = <&bpmp_clks TEGRA194_CLK_PWM2>;
37 		clock-names = "pwm";
38 		#pwm-cells = <2>;
39 		resets = <&bpmp_resets TEGRA194_RESET_PWM2>;
40 		reset-names = "pwm";
41 		status = "disabled";
42 	};
44 	tegra_pwm3: pwm@32a0000 {
45 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
46 		reg = <0x0 0x32a0000 0x0 0x10000>;
47 		nvidia,hw-instance-id = <0x2>;
48 		clocks = <&bpmp_clks TEGRA194_CLK_PWM3>;
49 		clock-names = "pwm";
50 		#pwm-cells = <2>;
51 		resets = <&bpmp_resets TEGRA194_RESET_PWM3>;
52 		reset-names = "pwm";
53 		status = "disabled";
54 	};
56 	tegra_pwm4: pwm@c340000 {
57 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
58 		reg = <0x0 0xc340000 0x0 0x10000>;
59 		nvidia,hw-instance-id = <0x3>;
60 		clocks = <&bpmp_clks TEGRA194_CLK_PWM4>,
61 			<&bpmp_clks TEGRA194_CLK_PLLAON>;
62 		clock-names = "pwm", "pll_aon";
63 		#pwm-cells = <2>;
64 		resets = <&bpmp_resets TEGRA194_RESET_PWM4>;
65 		reset-names = "pwm";
66 		status = "disabled";
67 	};
69 	tegra_pwm5: pwm@32c0000 {
70 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
71 		reg = <0x0 0x32c0000 0x0 0x10000>;
72 		nvidia,hw-instance-id = <0x4>;
73 		clocks = <&bpmp_clks TEGRA194_CLK_PWM5>,
74 		       <&bpmp_clks TEGRA194_CLK_PLLP_OUT0>,
75 		       <&bpmp_clks TEGRA194_CLK_CLK_M>;
76 		clock-names = "pwm", "parent", "slow-parent";
77 		#pwm-cells = <2>;
78 		resets = <&bpmp_resets TEGRA194_RESET_PWM5>;
79 		reset-names = "pwm";
80 		status = "disabled";
81 	};
83 	tegra_pwm6: pwm@32d0000 {
84 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
85 		reg = <0x0 0x32d0000 0x0 0x10000>;
86 		nvidia,hw-instance-id = <0x5>;
87 		clocks = <&bpmp_clks TEGRA194_CLK_PWM6>;
88 		clock-names = "pwm";
89 		#pwm-cells = <2>;
90 		resets = <&bpmp_resets TEGRA194_RESET_PWM6>;
91 		reset-names = "pwm";
92 		status = "disabled";
93 	};
95 	tegra_pwm7: pwm@32e0000 {
96 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
97 		reg = <0x0 0x32e0000 0x0 0x10000>;
98 		nvidia,hw-instance-id = <0x6>;
99 		clocks = <&bpmp_clks TEGRA194_CLK_PWM7>;
100 		clock-names = "pwm";
101 		#pwm-cells = <2>;
102 		resets = <&bpmp_resets TEGRA194_RESET_PWM7>;
103 		reset-names = "pwm";
104 		status = "disabled";
105 	};
107 	tegra_pwm8: pwm@32f0000 {
108 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
109 		reg = <0x0 0x32f0000 0x0 0x10000>;
110 		nvidia,hw-instance-id = <0x7>;
111 		clocks = <&bpmp_clks TEGRA194_CLK_PWM8>,
112 		       <&bpmp_clks TEGRA194_CLK_PLLP_OUT0>,
113 		       <&bpmp_clks TEGRA194_CLK_CLK_M>;
114 		clock-names = "pwm", "parent", "slow-parent";
115 		#pwm-cells = <2>;
116 		resets = <&bpmp_resets TEGRA194_RESET_PWM8>;
117 		reset-names = "pwm";
118 		status = "disabled";
119 	};

For the second question, device tree changes are not needed. Instead, please make sure they are configured to be used as PWM with the pinmux spreadsheet. However, as this is a third-party device, maybe you need to contact the vendor for more information.


Thank you for your reply. Could you please explain further how the GPIO pin can be derived from the device tree source you shared?
EDIT: It clicked for me that jetson-io references pwm8 (GPIO pin 13), pwm1 (GPIO pin 13), and pwm5 (GPIO pin 18). Are these number designators pwmX from the dtsi you shared? If this is the case I would still like some more clarification on how I can determine the GPIO pin from the spreadsheet (i.e. some clear way on knowing PWM#3 is tied to GPIO pin 18 on the 40p header., I can’t seem to figure this one out)

As for the second question/answer, do I need to change the spreadsheet and update the pinmux config on the board even if the jetson-io script indicated PWM is enabled?
If I do need to update the pinmux config on the board, could you tell me what I am supposed to do with the .cfg file generated by pinmux-dts2cfg.py?


As I said, because you are using a third-party device, please contact the vendor for more help.
Maybe you should check this:

It’s stated that pin 18 of the W13 expansion header is tied with PWM3.

It’s the same for the pinmux settings. The vendor may have done some customization on pinmux, which makes it different from NVIDIA’s DevKit.

Thanks Dave again for your reply.

Specifics of my board aside, could you please share how a GPIO pin can be derived from the dtsi snippet you shared? Trying to learn here, I just do not see a reference to an SoC pin in the device tree configs you sent.

Additionally, it is still unclear to me the relationship between the sysfs /sys/class/pwm/pwmchipX (0-4 on my system), and the output of cat /sys/kernel/debug/pwm:

platform/39c0000.tachometer, 1 PWM device
 pwm-0   ((null)              ): period: 0 ns duty: 0 ns polarity: normal

platform/32f0000.pwm, 1 PWM device
 pwm-0   ((null)              ): period: 0 ns duty: 0 ns polarity: normal

platform/32c0000.pwm, 1 PWM device
 pwm-0   ((null)              ): period: 20000000 ns duty: 7000000 ns polarity: normal

platform/c340000.pwm, 1 PWM device
 pwm-0   (pwm-fan             ): requested period: 45334 ns duty: 0 ns polarity: normal

platform/3280000.pwm, 1 PWM device
 pwm-0   ((null)              ): period: 0 ns duty: 0 ns polarity: normal

The debug output’s relationship to the dtsi snippet you shared is obvious, but I am unsure how to know which gpiochipX sysfs device each corresponds to.

Thank you in advance.

OK, so for example, we have the PWM controller at address 3280000 mapped to pwmchip0 in sysfs:

lrwxrwxrwx 1 root root 0 Mar 15 2023 pwmchip0 → …/…/devices/platform/3280000.pwm/pwm/pwmchip0

Then from the device tree source, we know it’s physical pin is PWM1, which is pin 13 of the expansion header of your H01 DevKit.

18 	tegra_pwm1: pwm@3280000 {
19 		compatible = "nvidia,tegra194-pwm", "nvidia,tegra186-pwm";
20 		reg = <0x0 0x3280000 0x0 0x10000>;
21 		nvidia,hw-instance-id = <0x0>;
22 		clocks = <&bpmp_clks TEGRA194_CLK_PWM1>,
23 		       <&bpmp_clks TEGRA194_CLK_PLLP_OUT0>,
24 		       <&bpmp_clks TEGRA194_CLK_CLK_M>;
25 		clock-names = "pwm", "parent", "slow-parent";
26 		#pwm-cells = <2>;
27 		resets = <&bpmp_resets TEGRA194_RESET_PWM1>;
28 		reset-names = "pwm";
29 		status = "disabled";
30 	};

Is that clear?


I think the main missing key for me that you helped me figure out was that pwmchip0 is a symlink, and you can see the 3280000 address (or offset? not sure what correct terminology is here) which maps back to the device tree source.

Thanks for taking the time to explain further.

Last Question:
If I was working with an original Nvidia produced Dev kit for AGX Xavier, would you expect that I would have to change the pinmux config to get PWM to work?

YES, please check the pinmux spreadsheet.


Thank you. I have since made more progress towards what I think needs to happen.
Reminder, I would like to use pin 18 on the GPIO header for PWM.
I have:
1.) Navigated to the Pinmux Spreadsheet for AGX Xavier, and changed GPIO35 “Customer Usage” to “GP_PWM5”, and the pin direction to “Output”.
2.) used Generate DTSI macro to create .dtsi files for pad voltage, GPIO, and pinmux.
3.) Used python script pinmux-dts2cfg.py to generate 2 .cfg files, one for GPIO and pinmux, and one for pad voltage.
4.) I over-wrote the pinmux configuration that is currently in my working dir:
I overwrote this with the GPIO and pinmux .cfg file I generated with the python script.
5.) I flashed just the kernel-dtb partition (I think that’s the right one?) to update the pinmux on the board.

Have I missed anything?
What is the recommended way to verify that I have actually made a change?

Appreciate the help in advance.


I think I am getting closer to what I need. I flashed the kernel-dtb partition like I said and then tried to run the simple_pwm.py script provided with the Jetson.GPIO package.

Now I seeing a square wave, but not exactly what I would expect. Scope capture attached.
Any further pointers would be appreciated. Maybe something with the pinmux configuration is off?
Thanks again in advance.

EDIT #2: I am not convinced my updated pinmux .cfg is making any significant change. I see similar behavior after flashing my new .cfg and the original one. Could use pointers as to what I am missing.
Also I think I should actually be flashing mb1 instead of kernel-dtb as according to documentation that is where pinmux is done.


YES, you should be flashing the MB1_BCT partition instead of kernel_dtb.
You can also verify whether the change is taking effect by checking registers with devmem2.


Thank is really a fantastic resource you linked, I wish I knew it existed last week!

I am learning more every day.
I have used this guide to take a look at the pinmux .cfg file and find the line that I think I am concerned with:
I think Header pin 18 corresponds to signal soc_gpio12 according to pinmux spreadsheet.
So, there is a line in my custom pinmux .cfg here:

pinmux.0x02434090 = 0x00000405; # soc_gpio12_ph0: gp, pull-down, tristate-disable, input-disable, lpdr-disable

In the original one, it reads:
pinmux.0x02434090 = 0x00000054; # soc_gpio12_ph0: rsvd0, pull-down, tristate-enable, input-enable, lpdr-disable

So it appears I am changing something.
I am flashing with:
sudo ./flash.sh -k MB1_BCT jetson-xavier mmcblk0p1
and changing the file:
Note I am not changing any padvoltage related files, as it does not seem that I have to?

Further, once I flash MB1_BCT and boot, I used devmem2 to check the register:

dev@tegra-ubuntu:~/jetson-gpio/samples$ sudo devmem2 0x02434090 /dev/mem opened. Memory mapped at address 0xffffa139a000. Value at address 0x2434090 (0xffffa139a090): 0x401
Which is similar but not the same as the 0x00000405 I was expecting. I guess there could a prod / other .cfg file overwriting this change?
Also do these pinmux settings look correct for setting PWM5? I would have expected some reference to the GP_PWM5 or something.

Further, if I flash the original .cfg file that comes with the BSP, The value is the same.
dev@tegra-ubuntu:~$ sudo devmem2 0x02434090 /dev/mem opened. Memory mapped at address 0xffff80023000. Value at address 0x2434090 (0xffff80023090): 0x401

I think I am getting close to a solution to my problem, so a couple more pointers would be greatly appreciated. Configuring pinmux is a first for me as you can probably tell.


I discovered that I was updating the wrong file, I should be updating:

I figured this out by grepping the flash process for pinmux and .cfg.
Comparing the original file with this name and my new one, I can see I am attempting to change the pinmux register from
pinmux.0x02434090 = 0x00000401; # soc_gpio12_ph0: gp, tristate-disable, input-disable, lpdr-disable
pinmux.0x02434090 = 0x00000405; # soc_gpio12_ph0: gp, pull-down, tristate-disable, input-disable, lpdr-disable

However, replacing this file and running a flash again, I only ever see 0x401 at that register. So my change is having no affect. Any reasons as to why would be appreciated!
Am I forgetting some command that I need to run after replacing cfgs?


I got the registers to update by doing a full flash. I have been getting some inconsistent update behavior by only flashing the MB1_BCT partition, not sure why.
I can succesfully update the register with the new pinmux.

I think it is at this point that the differences between the Nvidia dev kit and my Seeed brand are getting in the way. They provide a custom pinmux to support their board, so I don’t even know if I can use the pinmux spreadsheet provided by Nvidia.

It is a shame that Nvidia doesn’t sell the OG dev kit anymore, obviously I would have preferred to get that, but instead I had to go with this third party.

Thanks Dave for the help thus far - I will post back here with results so that others can benefit.

1 Like

Good luck to your development.

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