Skip to main content

Fixing UART DMA Issues on NVIDIA Jetpack 6.2.2

Introduction

This guide explains how to fix UART receive/transmit issues on NVIDIA Jetpack 6.2.2 platforms by forcing selected UART ports to run in PIO mode instead of DMA mode.

On affected JetPack 6 systems, some UART ports may receive corrupted data when the NVIDIA serial-tegra driver uses DMA. The workaround is to remove the dmas and dma-names properties from the relevant UART device-tree nodes using a device-tree overlay. When those properties are removed, the driver falls back to PIO mode.

This guide covers how to:

  • Identify the UART device-tree nodes.
  • Disable DMA for serial@3100000 and serial@3110000.
  • Compile the overlay into a .dtbo.
  • Install the overlay using the JetsonHacks jetson-orin-uart installer flow.
  • Verify that the UART ports are running in PIO mode after reboot.

What You Will Need

Before starting, make sure you have the following:

  • NVIDIA Jetson Orin device
    Jetson Orin Nano, Orin NX.

  • **JetPack 6.2.2 / L4T R36.5 This workaround is intended for JetPack 6 systems where the UART DMA issue appears.

  • Terminal access
    Local terminal or SSH access to the Jetson.

  • Root privileges
    The installation modifies files under /boot, so sudo is required.

  • device-tree-compiler
    The dtc tool is required to compile .dts overlays into .dtbo files.

  • JetsonHacks UART overlay repository
    The guide assumes the jetson-orin-uart repository is cloned on the Jetson.

Background: DMA Mode vs PIO Mode

Jetson UART ports are exposed through the Linux serial-tegra driver. Depending on the device-tree configuration, the driver may use DMA or PIO for UART data movement.

DMA Mode

In DMA mode, the UART driver uses the DMA engine to move UART data between hardware and memory. This reduces CPU involvement and is generally preferred for high-throughput serial traffic.

A UART node configured for DMA usually contains properties similar to:

dmas = <...>;
dma-names = "rx", "tx";

PIO Mode

PIO means Programmed I/O. In PIO mode, the CPU and UART driver move data through the UART registers directly, usually interrupt-driven.

The UART protocol, pins, baud rate, and Linux device name do not change. Only the internal data-transfer mechanism changes.

After the workaround is applied, the kernel log should show messages similar to:

serial-tegra 3100000.serial: RX in PIO mode
serial-tegra 3100000.serial: TX in PIO mode
serial-tegra 3110000.serial: RX in PIO mode
serial-tegra 3110000.serial: TX in PIO mode

Step 1: Clone the JetsonHacks UART Fix Repository

Clone the repository on the Jetson:

git clone https://github.com/jetsonhacks/jetson-orin-uart.git
cd jetson-orin-uart

The repository includes an install.sh script that:

  • Compiles disable-uart1-dma.dts into a device-tree overlay.
  • Copies the resulting .dtbo file to /boot.
  • Detects the board FDT.
  • Updates /boot/extlinux/extlinux.conf.
  • Adds a new boot entry while keeping the previous entry as a fallback.

Step 2: Replace the DTS Overlay

Create or replace the overlay source file:

cat > disable-uart1-dma.dts <<'EOF'
/dts-v1/;
/plugin/;

/ {
overlay-name = "Disable UART DMA for 3100000 and 3110000";

fragment@0 {
target-path = "/bus@0/serial@3100000";
delete_prop = "dmas", "dma-names";

__overlay__ {
status = "okay";
};
};

fragment@1 {
target-path = "/bus@0/serial@3110000";
delete_prop = "dmas", "dma-names";

__overlay__ {
status = "okay";
};
};
};
EOF

This overlay removes the DMA properties from both UART nodes:

/bus@0/serial@3100000
/bus@0/serial@3110000

Removing dmas and dma-names causes the serial-tegra driver to fall back to PIO mode for those UARTs.

NOTE: The command above overwrites disable-uart1-dma.dts. If you want to keep the original file, back it up first:

cp disable-uart1-dma.dts disable-uart1-dma.dts.bak

Step 3: Install device-tree-compiler

Install dtc if it is not already available:

sudo apt update
sudo apt install -y device-tree-compiler

Verify that dtc is installed:

dtc --version

Step 4: Compile the Overlay Manually

Compile the .dts file into a .dtbo file:

dtc -@ -I dts -O dtb -o disable-uart1-dma.dtbo disable-uart1-dma.dts

Verify that the .dtbo was generated:

ls -l disable-uart1-dma.dtbo

You can also decompile it to inspect the result:

dtc -I dtb -O dts disable-uart1-dma.dtbo

Step 5: Run the Installer

Run the JetsonHacks install script:

sudo bash install.sh

The installer compiles the overlay again and installs it into:

/boot/disable-uart1-dma.dtbo

It also modifies:

/boot/extlinux/extlinux.conf

The installer creates a new boot entry named UARTFix and leaves the previous boot entry available as a fallback.

Step 6: Reboot

Reboot the Jetson:

sudo reboot

After reboot, the UARTFix boot entry should load the overlay.

Step 7: Verify PIO Mode

After the system comes back up, check the kernel log:

sudo dmesg | grep -iE '3100000|3110000|pio|dma|serial-tegra'

Look for lines similar to:

serial-tegra 3100000.serial: RX in PIO mode
serial-tegra 3100000.serial: TX in PIO mode
serial-tegra 3110000.serial: RX in PIO mode
serial-tegra 3110000.serial: TX in PIO mode

If those lines appear, the UART ports are running in PIO mode.

Step 8: Confirm the Overlay Was Added to extlinux.conf

Check the boot configuration:

grep -nA20 -B5 "UARTFix" /boot/extlinux/extlinux.conf

You should see an entry that references:

/boot/disable-uart1-dma.dtbo

You can also check for the overlay path directly:

grep -n "disable-uart1-dma.dtbo" /boot/extlinux/extlinux.conf

Step 9: Inspect the Running Device Tree

The running device tree is available under /proc/device-tree.

Check whether the UART nodes still expose DMA properties:

ls /proc/device-tree/bus@0/serial@3100000 | grep dma || true
ls /proc/device-tree/bus@0/serial@3110000 | grep dma || true

If the overlay was applied correctly, dmas and dma-names should not appear for those nodes.

You can also decompile the running device tree:

sudo dtc -I fs -O dts /proc/device-tree -o /tmp/running.dts

Then inspect the UART sections:

grep -nA30 "serial@3100000" /tmp/running.dts
grep -nA30 "serial@3110000" /tmp/running.dts

One-Step Patch, Compile, and Install Script

Instead of running the steps manually, you can use this wrapper script inside the cloned jetson-orin-uart directory.

Create the script:

cat > fix-uart-dma-and-install.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DTS="${REPO_DIR}/disable-uart1-dma.dts"
DTBO="${REPO_DIR}/disable-uart1-dma.dtbo"
INSTALL="${REPO_DIR}/install.sh"

if [[ "${EUID}" -ne 0 ]]; then
echo "[ERROR] Run this script with sudo:"
echo " sudo bash $0"
exit 1
fi

if [[ ! -f "${INSTALL}" ]]; then
echo "[ERROR] install.sh not found in ${REPO_DIR}"
echo " Run this script from inside the jetson-orin-uart repo."
exit 1
fi

if ! command -v dtc >/dev/null 2>&1; then
echo "[INFO] Installing device-tree-compiler..."
apt update
apt install -y device-tree-compiler
fi

if [[ -f "${DTS}" ]]; then
cp "${DTS}" "${DTS}.bak.$(date +%Y%m%d%H%M%S)"
echo "[INFO] Backed up existing DTS."
fi

cat > "${DTS}" <<'DTS_EOF'
/dts-v1/;
/plugin/;

/ {
overlay-name = "Disable UART DMA for 3100000 and 3110000";

fragment@0 {
target-path = "/bus@0/serial@3100000";
delete_prop = "dmas", "dma-names";

__overlay__ {
status = "okay";
};
};

fragment@1 {
target-path = "/bus@0/serial@3110000";
delete_prop = "dmas", "dma-names";

__overlay__ {
status = "okay";
};
};
};
DTS_EOF

echo "[INFO] Wrote patched DTS:"
echo " ${DTS}"

echo "[INFO] Compiling local DTBO for verification..."
dtc -@ -I dts -O dtb -o "${DTBO}" "${DTS}"

echo "[INFO] Decompile check:"
dtc -I dtb -O dts "${DTBO}" 2>/dev/null | grep -E 'serial@3100000|serial@3110000|delete_prop|dma-names|dmas' || true

echo "[INFO] Running original install.sh..."
bash "${INSTALL}"

echo
echo "[INFO] Done."
echo "[INFO] Reboot to apply:"
echo " sudo reboot"
echo
echo "[INFO] After reboot, check:"
echo " sudo dmesg | grep -iE '3100000|3110000|pio|dma|serial-tegra'"
EOF

Make it executable and run it:

chmod +x fix-uart-dma-and-install.sh
sudo bash ./fix-uart-dma-and-install.sh

Summary

You now have a device-tree overlay that disables UART DMA for:

/bus@0/serial@3100000
/bus@0/serial@3110000

The overlay removes:

dmas
dma-names

from those UART nodes. Without DMA properties, the serial-tegra driver falls back to PIO mode.

The expected result after reboot is:

serial-tegra 3100000.serial: RX in PIO mode
serial-tegra 3100000.serial: TX in PIO mode
serial-tegra 3110000.serial: RX in PIO mode
serial-tegra 3110000.serial: TX in PIO mode

This confirms that both UART nodes are no longer using DMA.