I have a NanoPi R2S Plus. Armbian supports many of the NanoPi boards but doesn't have a configuration specific for the NanoPi R2S Plus. I have been running the NanoPi R2S configuration. They are similar boards but the Plus adds eMMC memory and has a different chip for the WAN network port so neither of these work.
Recently I started looking to add support for the NanoPi R2S Plus to Armbian to enable the eMMC and WAN port. It was the first time I had used the Armbian Build Framework. Previously I had only downloded pre-built images. What I discovered is that the build framework is very flexible but, therefore, complex and there is little documentation. This makes it difficult to work with: simple objectives are difficult to achieve.
Given the lack of documentation, copying working examples is the best way to get things done. There is support for over 300 boards in armbian/build. Configuration for some is seemingly simple while others are very sophisticated. But even the simple ones have a lot going on under the covers: the build framework itself is very sophisticated.
The problems with the NanoPi R2S Plus eMMC and WAN port were rooted in the device tree. But there is not one but two device trees to deal with: the device tree of the Linux kernel affects the booted system but u-boot has its own device tree that must be complete enough to boot Linux. In my case, I wanted to be able to boot from eMMC so needed to add that to both the u-boot and Linux device trees.
I had to add a new board. That required adding a configuration for it in the config/boards directory. To start, I copied the configuration for the NanoPi R2S to create config/boards/nanopi-r2s-plus.csc. This was the first step: the exact same configuration, just a different name. And learning to build an image with the compile.sh script.
Then I had to modify the device tree for the kernel, to enable the eMMC and WAN ports. FriendlyElec provides images for the NanoPi R2S Plus that have working eMMC and WAN port, including a Debian Bookworm image and images based on OpenWRT, one of which was pre-installed on the system. So I began to learn enough about device trees to be able to read and compare these and understand how the device tree for the NanoPi R2S is assembled by the Armbian build framework.
The syntax of the device tree is simple enough but the semantics and relations to the hardware and Linux kernel and device drivers is still bewildering. The device tree solves a complex problem and is necessarily complex itself. To master it, one must know intimate details of the hardware and its configuration and of the Linux drivers that read the device tree to get the information they require about the hardware. While I can compare and cut and paste the device tree source files, I am a long way from understanding the end-to-end interactions between the device drivers and the hardware. Fortunately, with all the examples available, it wasn't necessary to develop the device tree from hardware specs and the device driver code. All I had to do is copy the bits relevant to the eMMC and WAN port from the images where they worked into the Armbian build framework configuration I was creating for the NanoPi R2S Plus. Not quite trivial, but nearly so.
My first attempt to change the kernel device tree failed, yielding an image that wouldn't boot. It couldn't find an image to boot and failed back to TFTP/BOOTP. I never did find out what went wrong but I think a collection of incompatible patches applied to the kernel that, at the time, I didn't know persisted between builds.
I scrubbed my clone of armbian/build and second attempt produced a working image that I progressed through several modifications to get first the eMMC and then the WAN port working. This time, every image booted and worked as expected.
In addition to the new board configuration, I had to create device tree source (dts) file for the board. I did this two ways: first making one that included the dts for the NanoPi R2S in armbian/build then overrode the bits related to the eMMC and WAN port. But while doing this I saw that the Linux kernel itself has support for the NanoPi R2S Plus. It has working eMMC but the WAN port doesn't work. So I made a second version of dts for NanoPi R2S Plus that includes the dts from the kernel then overrides the bits related to the WAN port.
Ultimately, I only created two files: the board configuration and the dts.
The board configuration is 'config/boards/nanopi-r2s-plus.csc':
# Rockchip RK3328 quad core 1GB 2 x GBE USB2
BOARD_NAME="Nanopi R2S Plus"
BOARDFAMILY="rockchip64"
BOARD_MAINTAINER=""
BOOTCONFIG="nanopi-r2s-rk3328_defconfig"
KERNEL_TARGET="current,edge"
KERNEL_TEST_TARGET="current"
DEFAULT_CONSOLE="serial"
MODULES="g_serial"
MODULES_BLACKLIST="rockchipdrm analogix_dp dw_mipi_dsi dw_hdmi gpu_sched lima hantro_vpu"
SERIALCON="ttyS2:1500000,ttyGS0"
HAS_VIDEO_OUTPUT="no"
BOOT_FDT_FILE="rockchip/rk3328-nanopi-r2s-plus-a1.dtb"
This is a copy of the configuration for NanoPi R2S. I changed the BOARD_NAME and BOOT_FDT_FILE. 'dtb' is 'device tree blob' - a binary form of the device tree. The build framework creates this from the device tree source (dts) files. So I had to create the dts file that corresponds to the specified dtb.
First I created patch/kernel/archive/rockchip64-6.12/dt/rk3328-nanopi-r2s-plus-a1.dts.
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 FriendlyElec Computer Tech. Co., Ltd.
* (http://www.friendlyarm.com)
*/
/dts-v1/;
#include <dt-bindings/input/linux-event-codes.h>
#include "rk3328-nanopi-r2s.dts"
/ {
model = "FriendlyElec NanoPi R2S Plus";
compatible = "friendlyelec,nanopi-r2s-plus", "friendlyelec,nanopi-r2", "rockchip,rk3328";
gpio-keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
pinctrl-names = "default";
pinctrl-0 = <&gpio_key1>;
button@0 {
gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_LOW>;
label = "reset";
linux,code = <BTN_1>;
linux,input-type = <1>;
gpio-key,wakeup = <1>;
debounce-interval = <100>;
};
};
vcc_rtl8153: vcc-rtl8153-regulator {
compatible = "regulator-fixed";
gpio = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&usb30_en_drv>;
regulator-always-on;
regulator-name = "vcc_rtl8153";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
off-on-delay-us = <5000>;
enable-active-high;
};
};
&mach {
hwrev = <0>;
model = "NanoPi R2S";
};
&emmc {
status = "okay";
};
&i2c0 {
status = "okay";
};
&leds {
status = "okay";
led@2 {
gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>;
label = "lan_led";
};
led@3 {
gpios = <&gpio2 RK_PC2 GPIO_ACTIVE_HIGH>;
label = "wan_led";
};
};
&rk805 {
interrupt-parent = <&gpio1>;
interrupts = <RK_PD0 IRQ_TYPE_LEVEL_LOW>;
};
&vccio_sd {
status = "okay";
};
&io_domains {
vccio3-supply = <&vccio_sd>;
};
&sdmmc {
vqmmc-supply = <&vccio_sd>;
max-frequency = <150000000>;
sd-uhs-sdr50;
sd-uhs-sdr104;
status = "okay";
};
&sdmmc_ext {
status = "disabled";
};
&sdio_pwrseq {
status = "disabled";
};
&pinctrl {
pmic {
pmic_int_l: pmic-int-l {
rockchip,pins = <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
rockchip-key {
gpio_key1: gpio-key1 {
rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
usb {
otg_vbus_drv: otg-vbus-drv {
rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
};
usb30_en_drv: usb30-en-drv {
rockchip,pins = <2 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
/delete-node/ &rtl8211e;
&gmac2io {
phy-handle = <ðpy1>;
snps,reset-delays-us = <0 15000 50000>;
tx_delay = <0x22>;
rx_delay = <0x12>;
mdio {
compatible = "snps,dwmac-mdio";
#address-cells = <1>;
#size-cells = <0>;
ethpy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0x1>;
realtek,ledsel = <0xae00>;
};
};
};
This is a copy of the dts for the NanoPi R2s, with modifications to make the eMMC and WAN port work.
In the node for '&emmc' I changes status from "disabled" to "okay". That's all it took to get the eMMC working - all the relevant content was already there, just disabled because the NanoPi R2S doesn't have eMMC memory.
The changes to get the WAN port working are a little more complicated but I was able to copy them from the dts in the FriendlyElec kernel source. The changes are at the end of the file. First '/delete-node/ &rtl8211e;' which deletes the node for the rtl8211e chip that's in the NanoPi R2s, then the '&gmac2io' node that overrides some parameters and adds a new 'ethpy1' node for the rtl8211f chip that is in the NanoPi R2S Plus. I couldn't have written these myself. I don't know enough about the hardware or the device drivers, despite having downloaded technical papers that describe the differences between the rtl8211e and rtl8211f. But copy/paste is easy. I only had to figure out what were the relevant bits, which is simple, in hindsight, though it took me a while to learn enough about the device tree syntax.
This version is based on the support for NanoPi R2S in Armbian build. Through a chain of includes, it includes several other files from Armbian build.
My second implementation was based on the support in the kernel. I created patch/kernel/archive/rockchhip64-6.12/dt/rk3328-nanopi-r2s-plus-rev00.dts:
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2025 Ian Goodacre
*/
/dts-v1/;
#include "rk3328-nanopi-r2s-plus.dts"
/delete-node/ &rtl8211e;
&gmac2io {
phy-handle = <&rtl8211f>;
snps,reset-delays-us = <0 15000 50000>;
tx_delay = <0x22>;
rx_delay = <0x12>;
mdio {
rtl8211f: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0x1>;
realtek,ledsel = <0xae00>;
};
};
};
This includes the dts for NanoPi R2S Plus from the kernel, in which the eMMC is already enabled but the nodes for the WAN port are still for the old rtl8211e chip. So, again, I had to delete the node for rtl8211e, add a node for rtl8211f and override a very few parameters in the gmac2io node.
I chose the name of the dts to be clear what board it was for and with 'rev00' to avoid name conflict with the dts in the kernel and following the pattern in the FriendlyElec kernel of 'revXX' for various versions supporting various boards. The 'patch' gets added to the kernel source without changing any of the existing files.
With these changes I build a kernel that booted from SD card and had eMMC and WAN port both working.
Finally, I copied the dts file to patch/kernel/archive/rockchhip64-6.13/dt/rk3328-nanopi-r2s-plus-rev00.dts. 6.12 is the 'current' kernel and 6.13 is the 'edge' kernel, at least, currently. Then I build an 'edge' image and all was well.
The final complication is that I want to boot from eMMC rather than SD card. But u-boot device tree is separate from the kernel device tree and it, like the kernel device tree, was for the NanoPi R2S which doesn't have eMMC. So I had to modify the u-boot device tree. I found this harder because the way the u-boot source is patched is different and there was a little bug in the tool for generating the patch file that held me up for most of a day. But, ultimately, it isn't much more difficult to patch the dts in u-boot than in the kernel.
But, along the way, someone pointed out the more recent version of u-boot has native support for NanoPi R2S Plus. The configuration for NanoPi R2S was based on u-boot v2022.07 but v2025.01 has the support. It took me a while to learn enough to get the build for NanoPi R2S Plus to use the more recent version of u-boot but in the end the changes were simple.
I changed the board config: config/boards/nanopi-r2s-plus.csc:
# Rockchip RK3328 quad core 1GB 2 x GBE USB2
BOARD_NAME="Nanopi R2S Plus"
BOARDFAMILY="rockchip64"
BOARD_MAINTAINER=""
BOOTBRANCH="tag:v2025.01"
BOOTPATCHDIR="v2025.01"
BOOTCONFIG="nanopi-r2s-plus-rk3328_defconfig"
KERNEL_TARGET="current,edge"
KERNEL_TEST_TARGET="current"
DEFAULT_CONSOLE="serial"
MODULES="g_serial"
MODULES_BLACKLIST="rockchipdrm analogix_dp dw_mipi_dsi dw_hdmi gpu_sched lima hantro_vpu"
SERIALCON="ttyS2:1500000,ttyGS0"
HAS_VIDEO_OUTPUT="no"
BOOT_FDT_FILE="rockchip/rk3328-nanopi-r2s-plus-rev00.dtb"
BOOTBRANCH, BOOTPATCHDIR and BOOTCONFIG all affect the u-boot build. BOOTBRANCH specifies the branch of the u-boot repository, overriding the 'default' v2022.07 for rockchip64. And BOOTPATCHDIR specifies what directory to get u-boot patches from. The default directory contains patches for many boards, some of which are incompatible with v2025.01, causing build failure. There aren't many patches in the 'v2025.02' directory (patch/u-boot/v2025.01). All I had to do was add a directory for the NanoPi R2S Plus: patch/u-boot/v2025.01/board_nanopi-r2s-plus and put a patch file in it. The directory name is important. It is specific to the board, because it begins with 'board_' and the remainder matches the name of the board configuration file (I think that's where 'nanopi-r2s-plus' comes from).
In patch/u-boot/v2025.01/board_nanopi-r2s-plus I created patch file 001-add-trust-ini.patch:
diff --git a/trust.ini b/trust.ini
new file mode 100644
index 0000000..4af021a
--- /dev/null
+++ b/trust.ini
@@ -0,0 +1,15 @@
+[VERSION]
+MAJOR=1
+MINOR=0
+[BL30_OPTION]
+SEC=0
+[BL31_OPTION]
+SEC=1
+PATH=bl31.elf
+ADDR=0x10000
+[BL32_OPTION]
+SEC=0
+[BL33_OPTION]
+SEC=0
+[OUTPUT]
+PATH=trust.bin
This adds file 'trust.ini' to the root of the u-boot source, which is required to build the u-boot artifact. Before I added this patch the build failed because it couldn't find it. I just copied the patch from the configuration for v2022.07.
And with these changes, the build succeeded. It isn't tested yet, but I think it is getting close.
It took me a few weeks to work through all this, from setting up the build environment through learning about device trees, RockChip SOC booting, u-boot and the Armbian build framework. It looks simple in hindsight, but initially I was almost completely in the dark, stumbling about, breaking things.
No comments:
Post a Comment