I brought the initial RK3588S port to NSH on an Orange Pi 5 Pro. U-Boot and TF-A still do the firmware work; NuttX starts at the handoff described in the board guide.

The work was merged in Apache NuttX PR #19489 as three commits: RK3588 SoC support, the Orange Pi 5 Pro board and configurations, and the bring-up guide.

Why this board

I already had an Orange Pi 5 Pro and expected to use it, so it was a practical target. I started from the AM67A tree I had worked on at Baykar and Matteo Golin's initial Raspberry Pi 4B port, which had also been my reference for that earlier bring-up.

Where the port starts

The firmware still owns DRAM training, boot media, and the UART2 clock and pin setup. The path before NuttX is:

BootROM
  → DDR initialization / TPL
  → U-Boot SPL
  → TF-A BL31
  → U-Boot proper
  → NuttX

U-Boot transfers control with the MMU and data cache disabled. The observed handoff entered NuttX at EL2; the startup path then drops to EL1 before setting up the architecture state it needs.

Image placement and memory

The standard ARM64 Image header carries a load offset of 0x480000. U-Boot's booti command parses that header and relocates the binary to 0x02080000, the port's CONFIG_RAM_START.

The first version uses identity mappings and exposes a conservative 512 MiB RAM window. That is the configured CONFIG_RAM_SIZE, not an RK3588S hardware limit. I kept the initial map inside the available DRAM above the firmware reservations; the port does not yet consume the device tree.

Enough platform support to reach NSH

The initial support adds GICv3 interrupt support, the Arm generic timer, polling UART2, PSCI reset, and the board code needed to reach NSH. The initial MMU table is deliberately small:

UART2  device-nGnRnE
GICD   device-nGnRnE
GICR   device-nGnRnE
DRAM   normal memory

The early console does not reconfigure the UART. Firmware leaves UART2 usable, so arm64_lowputc() only polls the transmitter-ready bit and writes a byte.

The documented SD-card boot looks like this:

load mmc <dev>:1 0x02000000 /nuttx.bin
load mmc <dev>:1 ${fdt_addr_r} /boot/dtb/rockchip/rk3588s-orangepi-5-pro.dtb
booti 0x02000000 - ${fdt_addr_r}

What I tested

I tested both the Make and CMake/Ninja configurations on a 4 GiB Orange Pi 5 Pro. UART2 transmit and receive run at 1.5 Mbaud; the generic timer advances /proc/uptime and supports sleep; PSCI handles reboot; and OSTest completes successfully. The commands, observed handoff, and those hardware checks are recorded in the merged board guide.

What carried over from AM67A

This port moved quickly because I had done a harder version of the same job on AM67A at Baykar a year earlier. There, we were building the software stack around a new board at the same time. The NuttX, bootloader, and Linux sides were moving in parallel, and we had not settled every detail of the handoff before early bring-up started.

That showed up in the memory map. UART and other register space, SRAM, and DDR do not want the same MPU attributes. Some of those regions were initially described incorrectly, and the resulting failure did not point back to the map. Once we separated device memory from normal memory and fixed cacheability, the boot path started behaving. The public AM67A tree shows the region types we ended up with.

The Orange Pi was a different situation. Working Linux and U-Boot support already existed downstream; public board support included this U-Boot patch and Linux device tree. I applied board support for both and was adding NuttX on top of a known path, not bringing every layer up with it.

This time I wrote the memory map down before chasing the first byte: UART and GIC as device-nGnRnE, DRAM as normal memory. I also checked exactly what U-Boot would leave enabled at the handoff. It was the same checklist as AM67A, but this time I did not have to discover it after the first failure.

What the first port leaves out

This is an initial bring-up, not full board support. It does not provide most board peripherals, and the first version does not parse the DTB or start secondary cores.