How to Set USB 2.0 Eye Diagram Registers to Fixed Values in Device Tree or Boot Time

Hi NVIDIA team,

We would like to permanently set the USB 2.0 Eye Diagram related registers to fixed values during system boot, rather than applying them manually each time.

The registers and values we want to set are:

  • XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0 (Address: 0x03520088) → 0x2CC88D8
  • XUSB_PADCTL_USB2_OTG_PAD1_CTL_0_0 (Address: 0x035200C8) → 0x2CC88D8
  • XUSB_PADCTL_USB2_OTG_PAD2_CTL_0_0 (Address: 0x03520108) → 0x2CC88D8

Currently, we apply the values using the following commands:

sudo busybox devmem 0x03520088 32 0x2CC88D8
sudo busybox devmem 0x035200C8 32 0x2CC88D8
sudo busybox devmem 0x03520108 32 0x2CC88D8

Question:
Is there a way to configure these values permanently, such as through device tree (padctl settings) or a boot-time initialization script, so that they are applied automatically on each boot?

We are working on a Jetson Orin Nano, running L4T 36.4.3

Thanks in advance!

*** Please note that this reply is generated by LLM automatically ***

To permanently set the USB 2.0 Eye Diagram related registers to fixed values during system boot on a Jetson Orin Nano running L4T 36.4.3, you have a few options to consider. The goal is to automate the process of applying these register values, so they are set automatically on each boot, rather than manually executing commands each time.

Option 1: Device Tree Settings

The Device Tree is a data structure that describes the hardware components of a system, and it’s used by the operating system to configure and manage these components. Modifying the Device Tree to include your specific register settings is a viable approach. This involves creating or modifying a Device Tree Binary (DTB) file that includes the desired register settings.

For the Jetson Orin Nano, you would need to:

  1. Identify the Correct Device Tree File: Determine which DTB file is being used by your system. This information can be found in the boot logs or by checking the /boot directory.
  2. Modify the Device Tree Source: You’ll need to modify the Device Tree source files (.dts or .dtsi) to include your register settings. This requires knowledge of the Device Tree syntax and the specific register addresses you’re modifying.
  3. Compile the Device Tree: After modifying the source files, you’ll need to compile them into a DTB file using the Device Tree compiler (dtc).
  4. Flash the New DTB: Use the flash.sh command to flash the new DTB file to your device. Ensure you’re using the correct flash.sh command for your device and configuration.

Option 2: Boot-time Initialization Script

Another approach is to create a boot-time initialization script that applies these register settings automatically during each boot. This can be achieved by:

  1. Creating a Script: Write a script that applies the desired register settings using the appropriate commands (e.g., devmem2 or another tool suitable for your register modifications).
  2. Making the Script Executable: Ensure your script has execute permissions.
  3. Adding to Boot Process: You’ll need to add your script to the system’s boot process. This can typically be done by placing the script in a directory like /etc/rc.local or by adding it to a systemd service that runs at boot time.

Comparison and Considerations

  • Device Tree Modifications:

    • Pros: Applies settings at a very low level, potentially before the operating system fully boots. Can be more robust as it’s less dependent on the state of the system at boot time.
    • Cons: Requires knowledge of Device Tree syntax and compilation. Incorrect modifications can prevent the system from booting.
  • Boot-time Initialization Script:

    • Pros: Easier to implement for those familiar with scripting. Less risk of causing boot issues if done correctly.
    • Cons: May apply settings slightly later in the boot process, potentially after some system components have already been initialized.

Conclusion

Both methods can achieve your goal of permanently setting USB 2.0 Eye Diagram related registers on a Jetson Orin Nano. The choice between modifying the Device Tree and using a boot-time initialization script depends on your specific requirements, your team’s expertise, and the exact nature of the register settings you’re applying. If you’re looking for a more integrated, low-level solution, Device Tree modifications might be preferable. However, if ease of implementation and less risk of boot issues are more important, a boot-time script could be the better choice.

*** Please give the thumbs up if you get answers you like. Or provide feedback to help us improve the answer. ***

please refer to hardware/nvidia/t23x/nv-public/nv-soc/tegra234-soc-prod-overlay.dtsi

There is prod setting field for xusb.

We are tuning the USB 2.0 eye diagram for Jetson Orin NX/Nano modules, following the official application note:

  • Document: Jetson Orin NX / Nano Series Modules Tuning and Compliance Guide DA-11267-001 v1.2
  • Goal: Set the following registers to 0x2CC88D8 (HS_CURR_LEVEL bits [5:0] = 0x18 = 24) to pass USB eye diagram test:
0x03520088 (USB2_OTG_PAD0_CTL_0_0)
0x035200C8 (USB2_OTG_PAD1_CTL_0_0)
0x03520108 (USB2_OTG_PAD2_CTL_0_0)

DTS Setup:

We followed the guide and added the following to our Device Tree:

usb2-0 {
    nvidia,hs_curr_level_offset = <6>;
};
usb2-1 {
    nvidia,hs_curr_level_offset = <6>;
};
usb2-2 {
    nvidia,hs_curr_level_offset = <6>;
};

Issue 1: hs_curr_level_offset = 6 results in only a ~2 step change

After booting and checking register values with busybox devmem, we observed:

Before:
0x03520088 → 0x02CC88D1
0x035200C8 → 0x02CC88D0
0x03520108 → 0x02CC88D0

After adding offset = 6:
0x03520088 → 0x02CC88CF
0x035200C8 → 0x02CC88CF
0x03520108 → 0x02CC88CE

So even with offset = 6, the register only decreased by 2 or 3 (not the expected +6).


Issue 2: Reading USB_CALIB fuse returns 0xFFFFFFFF

We tried to read the USB_CALIB fuse:

sudo busybox devmem 0x038201F0

And it returned 0xFFFFFFFF.
Is this expected? If the fuse cannot be read, will the driver silently skip the offset calculation?


Issue 3: How does the driver apply the offset?

We are unsure how the kernel driver applies the nvidia,hs_curr_level_offset value.

  • Does it read fuse value + offset, then write to HS_CURR_LEVEL?
  • Or is it writing a fixed default value, ignoring the offset if fuse read fails?

If possible, could you point us to the relevant source code or logic?


Questions:

  1. If USB_CALIB fuse returns 0xFFFFFFFF, will it break the HS_CURR_LEVEL offset logic?
  2. What is the expected behavior of nvidia,hs_curr_level_offset in this case?
  3. Can we directly override HS_CURR_LEVEL instead of using an offset?
  4. Any reference driver code for how the offset is applied?

Hi,

Where did you get this value 0x038201F0?

It should be 0x038101f0

0x038201F0是錯的. 請你用0x038101f0

另外上面這個nvidia,hs_curr_level_offset在Jetpack6已經沒有支援了. 請直接把你要改的address/value放到我上面講的prod setting裡面

我參考以下兩個 overlay 設定檔,修改 USB2 pad 的暫存器值:

原始參考檔案:

tegra234-soc-prod-overlay.dtsi

xusb_padctl@3520000 {
    prod-settings {
        #prod-cells = <4>;
        prod {
            prod = <
                0 0x00000284 0x00000038 0x00000038              // BIAS_PAD_CTL_0
                0 0x00000288 0x03fff000 0x0051e000>;            // BIAS_PAD_CTL_1
        };
    };
};

tegra234-p3701-0000-prod-overlay.dtsi

xusb_padctl@3520000 {
    prod-settings {
        #prod-cells = <4>;
        prod {
            board {
                prod = <
                    0 0x00000088 0x01fe0000 0x00cc0000          // OTG_PAD0_CTL_0
                    0 0x00000094 0x0000000e 0x00000004          // OTG_PAD0_CTL_3
                    0 0x000000c8 0x01fe0000 0x00cc0000          // OTG_PAD1_CTL_0
                    0 0x000000d4 0x0000000e 0x00000004          // OTG_PAD1_CTL_3
                    0 0x00000108 0x01fe0000 0x00cc0000          // OTG_PAD2_CTL_0
                    0 0x00000114 0x0000000e 0x00000000          // OTG_PAD2_CTL_3
                    0 0x00000148 0x01fe0000 0x00cc0000>;        // OTG_PAD3_CTL_0
            };
        };
    };
};

我的修改:

我在 tegra234-p3768-0000.dtsi 中加入以下 prod-settings,嘗試直接將三個 USB2 OTG PAD 設為固定值 0x02CC88D8

xusb_padctl@3520000 {
    prod-settings {
        #prod-cells = <4>;
        prod {
            prod = <
                0 0x00000088 0xffffffff 0x02CC88D8      // OTG_PAD0_CTL_0
                0 0x000000c8 0xffffffff 0x02CC88D8      // OTG_PAD1_CTL_0
                0 0x00000108 0xffffffff 0x02CC88D8>;    // OTG_PAD2_CTL_0
        };
    };
};

我也有試過保留 board {} 包起來與否,兩者結果一致。結果觀察(透過 devmem 驗證):
Before:
0x03520088 → 0x02CC88D1
0x035200C8 → 0x02CC88D0
0x03520108 → 0x02CC88D0

After :
0x03520088 → 0x2CC88D8 這是預期的值
0x035200C8 → 0x2CC88D8 這是預期的值
0x03520108 → 0x02CC88CE 這不是預期的值

問題:

第三個 register (0x03520108) 寫入後的值是 0x02CC88CE,不是預期的 0x02CC88D8,目前還不確定是否被其他設定覆蓋,或是在 bootloader 某個階段被重寫

我們在 tegra234-soc-prod-overlay.dtsi 中新增了以下設定:

xusb_padctl@3520000 {
    prod-settings {
        #prod-cells = <4>;
        prod {
            prod = <
                0 0x00000088 0x00000008 0x00000008     // XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0
                0 0x000000c8 0x00000008 0x00000008     // XUSB_PADCTL_USB2_OTG_PAD1_CTL_0_0
                0 0x00000108 0x00000008 0x00000008     // XUSB_PADCTL_USB2_OTG_PAD2_CTL_0_0
                0 0x00000284 0x00000038 0x00000038     // XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0
                0 0x00000288 0x03fff000 0x0051e000     // XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_0
            >;
        };
    };
};

期望修改後的暫存器值如下:

0x03520088 → 0x02CC88D8
0x035200C8 → 0x02CC88D8
0x03520108 → 0x02CC88D8

原始值為:

0x03520088 → 0x02CC88D1
0x035200C8 → 0x02CC88D0
0x03520108 → 0x02CC88D0

但不論將 mask 寫成 0x000000080x0000000F0xFFFFFFFF, apply overlay 後,值都變成如下:

0x03520088 → 0x02CC88CF
0x035200C8 → 0x02CC88CF
0x03520108 → 0x02CC88CE

我們預期只變更 bit 3,但實際卻連 bit 0~2 也被清除。請問這是因為 overlay 機制自動清除某些 bit?還是這些暫存器受限於特定硬體條件、無法透過 prod-setting 如期設定?謝謝!

請問有任何進展嗎?

謝謝

Hi,

我們發現原先的prod setting有點問題.

請照下列patch把prod setting的device tree先修正 再加上你的prod內容

diff --git a/nv-platform/tegra234-p3701-0000-prod-overlay.dtsi b/nv-platform/tegra234-p3701-0000-prod-overlay.dtsi
index 6d4b9b6..554c4bf 100644
--- a/nv-platform/tegra234-p3701-0000-prod-overlay.dtsi
+++ b/nv-platform/tegra234-p3701-0000-prod-overlay.dtsi
@@ -226,7 +226,7 @@
 			};
 		};
 
-		xusb_padctl@3520000 {
+		padctl@3520000 {
 			prod-settings {
 				#prod-cells = <4>;
 				prod {
diff --git a/nv-soc/tegra234-soc-prod-overlay.dtsi b/nv-soc/tegra234-soc-prod-overlay.dtsi
index 7725c61..8777024 100644
--- a/nv-soc/tegra234-soc-prod-overlay.dtsi
+++ b/nv-soc/tegra234-soc-prod-overlay.dtsi
@@ -540,7 +540,7 @@
 			};
 		};
 
-		xusb_padctl@3520000 {
+		padctl@3520000 {
 			prod-settings {
 				#prod-cells = <4>;
 				prod {

我在 tegra234-soc-prod-overlay.dtsi 中新增了以下設定,希望修改 USB2 PAD 控制暫存器的值:

padctl@3520000 {
			prod-settings {
				#prod-cells = <4>;
				prod {
					prod = <
					         0 0x00000088 0x0000000f 0x00000008              //XUSB_PADCTL_USB2_OTG_PAD0_CTL_0_0
                             0 0x000000c8 0x0000000f 0x00000008              //XUSB_PADCTL_USB2_OTG_PAD1_CTL_0_0
                             0 0x00000108 0x0000000f 0x00000008              //XUSB_PADCTL_USB2_OTG_PAD2_CTL_0_0
			                  ....
				};

我期望以下三個暫存器值都變成 0x02CC88D8

0x03520088 → 0x02CC88D8 ✅
0x035200C8 → 0x02CC88D8 ✅
0x03520108 → 0x02CC88D8 ❌

但實際上第 3 個 (0x03520108) 沒有更新,仍為原本的值 0x02CC88D0。這三筆設定使用的是相同的 mask (0x0F) 和 value (0x08),其他兩筆都有生效。

請問:

  1. prod-settings 是不是只有在 PAD 被啟用時才會套用?
  2. 若該 PAD 預設未啟用,有什麼方式可以強制套用該設定?

謝謝協助!

Hi,

能請你確認一下

→ kernel/3rdparty/canonical/linux-jammy/kernel-source/drivers/phy/tegra/xusb-tegra186.c

→ tegra186_utmi_phy_power_on

在你那邊有沒有處理到index =2 的情況嗎? 這個是XUSB_PADCTL_USB2_OTG_PAD2_CTL_0_0.

1 Like

Hi WayneWWW,

我參考了以上步驟同樣遇到USB2_PAD2無法設定值的問題,
在{L4T}/source/kernel/kernel-jammy-src/drivers/phy/tegra/xusb-tegra186.c中
itegra186_utmi_phy_power_on() 的lane→index是取自phy driver private data,

您好,我檢查了 tegra186_utmi_phy_power_on() 函數,確實有根據 index 使用 tegra_prod_set_by_name() 嘗試套用 prod_c_utmi%d 的設定:

unsigned int index = lane->index;

sprintf(prod_name, “prod_c_utmi%d”, index);
err = tegra_prod_set_by_name(&padctl->regs, prod_name, priv->prod_list);

而原本的 DTS 裡只有一個通用的 prod 區塊,並沒有針對 index=2(也就是 PAD2)加入 prod_c_utmi2,
所以推測 driver 在 power on PAD2 時,找不到對應的設定而跳過。
想確認一下,是否可以這樣新增設定來補上 PAD2 的設定?

prod_c_utmi2 {
prod = <
0 0x00000108 0x0000000f 0x00000008
>;
};

請問這樣的做法是否正確?

謝謝!

Hi,

We should consider backporting the tegra_xusb_lane_parse_dt function from JP5.1.5’s xusb.c to restore support for the "nvidia,hs_curr_level_offset" device tree property, so that the driver will use “usb2->hs_curr_level_offset” to update the “XUSB_PADCTL_USBX_OTG_PAD0_CTL_0_0” register.

int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
			     struct device_node *np)
{
	struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
	struct device *dev = &lane->pad->dev;
	const char *function;
	s32 offset;
	int err;

	err = of_property_read_string(np, "nvidia,function", &function);
	if (err < 0)
		return err;

	err = match_string(lane->soc->funcs, lane->soc->num_funcs, function);
	if (err < 0) {
		dev_err(dev, "invalid function \"%s\" for lane \"%pOFn\"\n",
			function, np);
		return err;
	}

	lane->function = err;

	err = of_property_read_s32(np, "nvidia,hs_curr_level_offset", &offset);
	if (err == 0)
		usb2->hs_curr_level_offset = offset;

	/* this property is optional, ignore -EINVAL error */
	if (err == -EINVAL)
		err = 0;

	return err;
}

Hi @mei198309

沒有. 先不用管prod_c_utmi%d

prod_c_utmi%d 下面那一段應該會處理你前面寫的那種形式.

(我的意思是下面這段應該會處理)

896  		err = tegra_prod_set_by_name(&padctl->regs, "prod",
897  					     priv->prod_list);
898  		if (err)
899  			dev_dbg(dev, "failed to apply prod settings\n");

之後到了更下面一點的部份

>padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));

這裡如果有index =2的情況才會處理到你那個XUSB_PADCTL_USB2_OTG_PAD2_CTL_0_0.

現在是請你加一些print在driver裡面確認一下值有沒有被讀進去+ 是否後續有被其他東西改到

我補充了一些 debug log,目前觀察到以下情況:

驅動在 tuning 寫入前讀到的是 PADx_CTL0 的初始值,這應該是 DTS 中 tegra234-soc-prod-overlay.dtsi 裡的 prod-settings 還沒套用之前的值。

value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
	dev_info(dev, "[USB2 Tuning] Before write PAD%d_CTL0: 0x%08x\n", index, value);
	
	value &= ~USB2_OTG_PD_ZI;
	value |= TERM_SEL;
	value &= ~HS_CURR_LEVEL(~0);

	if (usb2->hs_curr_level_offset) {
		int hs_current_level;

		hs_current_level = (int)priv->calib.hs_curr_level[index] +
						usb2->hs_curr_level_offset;

		if (hs_current_level < 0)
			hs_current_level = 0;
		if (hs_current_level > 0x3f)
			hs_current_level = 0x3f;

		value |= HS_CURR_LEVEL(hs_current_level);
	} else {
		value |= HS_CURR_LEVEL(priv->calib.hs_curr_level[index]);
	}

	padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
        dev_info(dev, "[USB2 Tuning] After  write PAD%d_CTL0\n", index);
        value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
        dev_info(dev, "[USB2 Tuning]  Read back PAD%d_CTL0: 0x%08x\n", index, value);
        p@p-desktop:~$ sudo dmesg | grep "USB2 Tuning"
[    3.689134] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] successfully applied prod settings
[    3.689142] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] Before write PAD0_CTL0: 0x02cc88e8
[    3.689145] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] After  write PAD0_CTL0
[    3.689147] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning]  Read back PAD0_CTL0: 0x02cc88d1
[    3.689514] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] successfully applied prod settings
[    3.689520] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] Before write PAD1_CTL0: 0x02cc88e8
[    3.689521] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] After  write PAD1_CTL0
[    3.689523] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning]  Read back PAD1_CTL0: 0x02cc88d0
[    3.689549] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] successfully applied prod settings
[    3.689555] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] Before write PAD2_CTL0: 0x02cc88e8
[    3.689556] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning] After  write PAD2_CTL0
[    3.689559] tegra-xusb-padctl 3520000.padctl: [USB2 Tuning]  Read back PAD2_CTL0: 0x02cc88d0

從 DTS 看起來,PAD0、PAD1、PAD2 都有設定:

padctl@3520000 {
    prod-settings {
        #prod-cells = <4>;
        prod {
            prod = <
                0 0x00000088 0x0000000f 0x00000008 // PAD0
                0 0x000000c8 0x0000000f 0x00000008 // PAD1
                0 0x00000108 0x0000000f 0x00000008 // PAD2
            >;
        };
    };
};

但實際 boot 完後用 devmem 讀的結果是:

PAD0: 0x02CC88D8
PAD1: 0x02CC88D8
PAD2: 0x02CC88D0 ← 這個值沒被改

想請問:請問這樣的行為是否合理?還是有其他建議可以進一步確認是誰在改掉?

謝謝!

在tegra186_utmi_phy_power_on最後還有一個tegra186_utmi_pad_power_on.

裡面也會對XUSB_PADCTL_USB2_OTG_PADX_CTL0做操作, 也請確認一下這裡.