RK3399 新设计工控机配置指南

用时两周,总算将新的 RK3399 工控机配置好了,期间遇到各种坑,在此记录一二。

RK3399 工控机硬件配置

首先看下硬件配置。

接口 数量 说明
TypeC 1个 OTG 口,可用于烧录固件
USB3.0 4个
USB2.0 2个
RTC芯片 hym8563 1个 用于RTC时钟存储
SD 1个 可插入SD/TF 卡
HDMI 1个 可接显示器
以太网口 2个 1个千兆,1个百兆
串口 3个 UART0, UART2, UART4, 其中UART2 作为console,波特率1500000
cp210x 1个 对应 /dev/ttyUSB0
pwm 1个 对应 pwm3
led 2个 电源指示灯,状态指示灯
按键 2个 1个复位按键,1个recover按键
DC-12 电源 1个 12V直流电源,rk808 电源芯片

kernel 启动失败

首次编译SDK通过后,烧录固件至工控机,uboot正常执行,但是kernel执行3s左右停止。停止的位置固定,但是没有错误信息,感觉像是突然停止,停止前面是USB驱动加载信息,停止后不会自动重启。这个问题困扰我很久。

[    1.963510] hub 4-0:1.0: USB hub found
[    1.964024] hub 4-0:1.0: 1 port detected
[    1.966154] dwmmc_rockchip fe310000.dwmmc: IDMAC supports 32-bit address mode.
[    1.966842] dwmmc_rockchip fe310000.dwmmc: Using internal DMA controller.
[    1.967451] dwmmc_rockchip fe310000.dwmmc: Version ID is 270a
[    1.968004] dwmmc_rockchip fe310000.dwmmc: DW MMC controller at irq 25,32 bit host data width,256 deep fifo
[    1.968898] dwmmc_rockchip fe310000.dwmmc: 'clock-freq-min-max' property was deprecated.
[    1.969733] dwmmc_rockchip fe310000.dwmmc: No vmmc regulator found
[    1.970282] dwmmc_rockchip fe310000.dwmmc: No vqmmc regulator found
[    1.971302] dwmmc_rockchip fe310000.dwmmc: allocated mmc-pwrseq
[    1.985263] mmc_host mmc1: Bus speed (slot 0) = 400000Hz (slot req 400000Hz, actual 400000HZ div = 0)
[    1.999140] dwmmc_rockchip fe310000.dwmmc: 1 slots initialized
[    2.001576] dwmmc_rockchip fe320000.dwmmc: IDMAC supports 32-bit address mode.
[    2.002303] dwmmc_rockchip fe320000.dwmmc: Using internal DMA controller.
[    2.002958] dwmmc_rockchip fe320000.dwmmc: Version ID is 270a
[    2.003537] dwmmc_rockchip fe320000.dwmmc: DW MMC controller at irq 26,32 bit host data width,256 deep fifo
[    2.004488] dwmmc_rockchip fe320000.dwmmc: 'clock-freq-min-max' property was deprecated.
[    2.005456] dwmmc_rockchip fe320000.dwmmc: No vmmc regulator found
[    2.006611] dwmmc_rockchip fe320000.dwmmc: allocated mmc-pwrseq
[    2.007435] rockchip-iodomain ff770000.syscon:io-domains: Setting to 3300000 done
[    2.008482] rockchip-iodomain ff770000.syscon:io-domains: Setting to 3300000 done
[    2.022019] mmc_host mmc2: Bus speed (slot 0) = 400000Hz (slot req 400000Hz, actual 400000HZ div = 0)
[    2.026205] mmc_host mmc1: Bus speed (slot 0) = 300000Hz (slot req 300000Hz, actual 300000HZ div = 0)
[    2.035890] dwmmc_rockchip fe320000.dwmmc: 1 slots initialized
[    2.040194] input: gpio-keys as /devices/platform/gpio-keys/input/input2
[    2.041572] ==gsl_ts_init==
[    2.041975] ret=0
[    2.043363] rk808-rtc rk808-rtc: setting system clock to 2013-01-19 05:34:15 UTC (1358573655)
[    2.066790] mmc_host mmc1: Bus speed (slot 0) = 200000Hz (slot req 200000Hz, actual 200000HZ div = 0)
[    2.078907] u?
[    2.111206] phy phy-ff770000.syscon:usb2-phy@e450.6: charger = USB_SDP_CHARGER
[    2.115781] rockchip-dwc3 usb0: USB peripheral connected
[    2.151998] usb 1-1.2: new full-speed USB device number 3 using ehci-platform
[    2.175032] usb 5-1: new high-speed USB device number 2 using xhci-hcd

由于是新设计的板子,所以无法确定是硬件问题还是软件问题,假定硬件没问题,那就是软件配置问题,于是我逐一排查DTS中gpio的配置,比对工控机原理图,发现 SYR837 CPU 电源管理芯片的 gpio不匹配。原理图中配置如下:

syr837

CPU_B_SLEEP_H 对应管脚如下:

CPU_B_SLEEP_H

这里是GPIO1_C1, 在dts中表示为 GPIO1_17, 实际dts中配置为 GPIO1_18, 所以不匹配,我将io修改后还是不行,怀疑是不是SPI功能复用所致,所以也确保SPI功能都disabled了,但是还是失败。

不过gpio修改后,发现log变了,kernel出现了 crash。log忘记保存了,根据crash信息,根据错误码排查到设备busy,说明有可能gpio被其它设备占用了,最后排查发现是 vcc3v3_pcie 节点也使用了 GPIO1_17, got you! 把这个disabled掉就正常了!而且这个pcie电源也没有被其它节点引用,所以禁用也不影响其它功能。

小结: kernel 启动失败的原因是cpu电源芯片的gpio配置错误,同时正确gpio还被vcc3v3_pcie 占用了,导致CPU供电异常,最后kernel停止运行。解决方案是修改syr837 gpio配置,禁用 vcc3v3_pcie

--- a/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
@@ -239,6 +239,7 @@

        vcc3v3_pcie: vcc3v3-pcie-regulator {
                compatible = "regulator-fixed";
+               status = "disabled";
                enable-active-high;
                regulator-always-on;
                regulator-boot-on;
@@ -447,7 +448,7 @@                                                                                         
                regulator-min-microvolt = <712500>;
                regulator-max-microvolt = <1500000>;
                regulator-ramp-delay = <1000>;
-               vsel-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
+               vsel-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>;
                fcs,suspend-voltage-selector = <1>;
                regulator-always-on;
                regulator-boot-on;
@@ -945,7 +946,7 @@

 &rockchip_suspend {
        rockchip,power-ctrl =
-               <&gpio1 18 GPIO_ACTIVE_LOW>,
+               <&gpio1 17 GPIO_ACTIVE_LOW>,
                <&gpio1 14 GPIO_ACTIVE_HIGH>;
 };

USB 3.0 供电失败

kernel起来了,系统也跑起来了,接下来就是硬件接口配置了,首先测了测USB功能,使用串口设备插入USB接口,发现电源指示灯不亮,说明没有供电。根据原理图找到所有usb接口的电源使能管脚。

gpio gpio1_num io_num description
GPIO1_B1 gpio1_9 41 VCC5V0_USB3-1_EN
GPIO1_B2 gpio1_10 42 VCC5V0_USB3-2_EN
GPIO1_B5 gpio1_13 45 VCC5V0_USB3-3_EN
GPIO1_C2 gpio1_18 50 VCC5V0_USB3-4_EN

然后通过 /sys/class/gpio 去使能。

#!/bin/bash
cd /sys/class/gpio

# usb3.0-1~4: 41 42 45 50
gpios=(41 42 45 50)

for io in ${gpios[@]}
do
    echo $io > export
    echo out > gpio$io/direction
    echo 1 > gpio$io/value
done

发现每次写入 1 后,读出来还是0,说明IO的输出不受控制,于是我测试了 gpio1 的其它io,部分可控,部分不可控。

为此,我使用 io 指令,结合 RK3399 寄存器手册,查看 gpio 的复用情况和寄存器配置情况是否正常。

查询usb gpio复用功能

gpio1_Bx 接口为例,在 TRM 文档中搜索 gpio1b, 找到 io 复用寄存器地址。

pmugrf_gpio1b

这里有几个关键信息, PMUGRF 将用于查找基地址,offset 0x00014PMUGRF_GPIO1B_IOMUX 的偏移量。

那么如何查找基地址呢?其实文档的第一张图就是地址映射图 Address Mapping。从中可以找到 PMUGRF的基地址,当然也可以找到其它寄存器的基地址。

pmugrf

从图中知道基地址为 0xFF320000, 加上偏移量 0x00014 就等于 0xFF320014.

最后通过 io 指令去查询。

# iomux
# PMUGRF Base: 0xff320000
# PMUGRF_GPIO1B_IOMUX offset: 0x00014

# read iomux
root@firefly:~# io -4 -r 0xff320014
ff320014:  00008141

查询结果是 0x00008141, 这个值该怎么读呢,这个是32位数据,对应gpio1_b0~gpio1_b7 共8个gpio的复用情况。每两个bit代表一个gpio。详情查看TRM文档中 PMUGRF_GPIO1B_IOMUX 寄存器说明。

gpio1b_iomux

对应 0x00008141, 可以解析出每个gpio的复用情况。

# 0x8141:      10           00    00    01              01              00    00    01
# gpio1_B7~B0: i2c0pmu_scl, gpio, gpio, i2c4sensor_scl, i2c4sensor_sda, gpio, gpio, uart4m0_sout
#              B7           B6    B5    B4              B3              B2    B1    B0

最终查询到USB 3.0 gpio1_b1, gpio1_b2 , gpio1_b5 均为 GPIO 模式,没有被复用,说明配置正常。

查询USB gpio value

既然复用寄存器没有问题,那再来看看 gpio value配置,看看使用 sys/class/gpio/gpioxx/value 配置有没有生效。

与查询复用情况类似,首先找到 gpio1的基地址。

gpio1_base

基地址为 0xFF730000, 接着查看gpio 数据寄存器 GPIO_SWPORTA_DR 的偏移量。

gpio1_offset

DR 寄存器偏移量为 0x0000, 32位寄存器对应gpio1 32个gpio的数据。接下来使用 io 指令查询。

# gpio1 base: 0xff730000
# DR offset: 0x00000000
# DDR offset: 0x00000004

# read DR
root@firefly:~# io -4 -r 0xff730000
ff730000:  00042602
# 0x42602: 0x0100 0010 0110 0000 0010
#               C0        B0        A0

# read DDR
root@firefly:~# io -4 -r 0xff730004
ff730004:  00062602
# 0x62602: 0x0110 0010 0110 0000 0010
#               C0        B0        A0

# 说明 gpio 的 direction, value 均配置正常

以上查询到 DRDDR 的配置情况,均正常。

USB 3.0 gpio 电源域配置

到此为止,io配置、io查询、复用功能确认都正常,但是实际输出就是不对。最后只能在 RK redmine 系统中提交issues,根据他们提供的技术文档,发现io输出不受控的问题可能与电源域配置有关。

首先查看原理图,gpio1 的电源接口。

gpio1_power

gpio1 使用 PMUIO2 电源,电压1.8v。然后查看 dts 中的配置。发现是 3.0v, 果然不一样!

       uboot-set = <RK3399_PMU1830_VDD_3V0>;
       pmu1830-supply = <&vcc_3v0>;

既然问题找到了,那解决方案就很明显了。

解决方案

修改 pmu1830 电源域配置。修改后,USB3.0 供电一切正常。

--- a/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
@@ -866,8 +866,8 @@
         * also list here for more convenient configuration:
         * pmu1830-supply: RK3399_PMU1830_VDD_1V8 or RK3399_PMU1830_VDD_3V0
         */
-       uboot-set = <RK3399_PMU1830_VDD_3V0>;
-       pmu1830-supply = <&vcc_3v0>;
+       uboot-set = <RK3399_PMU1830_VDD_1V8>;
+       pmu1830-supply = <&vcc_1v8>;
 };

 &pinctrl {

USB 2.0 无法识别串口设备

USB3.0 问题是电源域的问题, USB2.0 则不太一样, USB2.0 供电正常,但是无法识别设备,而且不是所有接口都不能识别,两个接口,一个可以,一个不可以,但是它们共用一个usb hub,没道理会这样。最终由我老大发现是硬件问题,老大通过修改硬件电路解决了,perfect!

TypeC 接口配置

DTS默认的typec的电源使能接口 vbus-5v-gpios 配置与原理图中是不匹配的,需要更正一下。

--- a/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
@@ -739,7 +739,7 @@
                pinctrl-names = "default";
                pinctrl-0 = <&fusb0_int>;
                int-n-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
-               vbus-5v-gpios = <&gpio2 0 GPIO_ACTIVE_HIGH>;
+               vbus-5v-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
                status = "okay";
        };

以太网配置

工控机包含两个以太网,默认千兆网没有打开,需要使能 gmac, 使能后,两个网口皆开,对应 eth0, eth1. 但是eth0 打开up失败。

root@firefly:~# ifconfig eth0 up
[  128.132426] stmmac_open: Cannot attach to PHY (error: -19)
SIOCSIFFLAGS: No such device

最后排查是被其它设备占用io,禁用 uart1, uart3 后正常。

brctl 失败

由于包含两个网口,可能会用到网桥功能,我安装 brctl 后却发现无法使用, 提示参数错误。使用 strace 排查。

root@firefly:~# brctl addbr br0
add bridge failed: Invalid argument
root@firefly:~# strace brctl addbr br0
execve("/sbin/brctl", ["brctl", "addbr", "br0"], [/* 18 vars */]) = 0
brk(0)                                  = 0x766000
uname({sys="Linux", node="firefly", ...}) = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7237000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=62149, ...}) = 0
mmap2(NULL, 62149, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf720a000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/arm-linux-gnueabihf/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\215w\1\0004\0\0\0"..., 512) = 512
lseek(3, 908188, SEEK_SET)              = 908188
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 2880) = 2880
lseek(3, 904740, SEEK_SET)              = 904740
read(3, "A4\0\0\0aeabi\0\1*\0\0\0\0057-A\0\6\n\7A\10\1\t\2\n\3\f"..., 53) = 53
fstat64(3, {st_mode=S_IFREG|0755, st_size=911068, ...}) = 0
mmap2(NULL, 947624, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xf7122000
mprotect(0xf71fd000, 28672, PROT_NONE)  = 0
mmap2(0xf7204000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xda000) = 0xf7204000
mmap2(0xf7207000, 9640, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xf7207000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7236000
set_tls(0xf7236850, 0xf723a058, 0xf7236f38, 0xf7236850, 0xf723a058) = 0
mprotect(0xf7204000, 8192, PROT_READ)   = 0
mprotect(0x14000, 4096, PROT_READ)      = 0
mprotect(0xf7239000, 4096, PROT_READ)   = 0
munmap(0xf720a000, 62149)               = 0
socket(PF_LOCAL, SOCK_STREAM, 0)        = 3
ioctl(3, SIOCBRADDBR, 0xffb29954)       = -1 ENOPKG (Package not installed)
ioctl(3, SIOCSIFBR, 0xffb28848)         = -1 EINVAL (Invalid argument)
write(2, "add bridge failed: Invalid argum"..., 36add bridge failed: Invalid argument
) = 36
exit_group(1)                           = ?
+++ exited with 1 +++
root@firefly:~#

定位到 ENOPKG 错误,提示 package not installed

socket(PF_LOCAL, SOCK_STREAM, 0)        = 3
ioctl(3, SIOCBRADDBR, 0xffb29954)       = -1 ENOPKG (Package not installed)
ioctl(3, SIOCSIFBR, 0xffb28848)         = -1 EINVAL (Invalid argument)

搜索相关信息得知是内核配置不包含 CONFIG_BRIDGE 所致,添加该选项并重新编译内核后解决。 brctl 正常执行。

brctl addbr br0
brctl addif br0 eth0
brctl addif br0 eth1
ifconfig br0 192.168.64.20 up

添加 USB wifi 支持

由于工控机不自带wifi模块,可能会用到usb wifi网卡,所以需要添加驱动支持。

--- a/arch/arm64/configs/rockchip_linux_defconfig
+++ b/arch/arm64/configs/rockchip_linux_defconfig
@@ -173,12 +173,13 @@ CONFIG_USB_NET_CDC_MBIM=y
 # CONFIG_USB_NET_ZAURUS is not set
 CONFIG_LIBERTAS_THINFIRM=y
 CONFIG_USB_NET_RNDIS_WLAN=y
+CONFIG_RTL8188EE=y
 CONFIG_RTL8192CU=y
 CONFIG_WL_ROCKCHIP=y
 CONFIG_WIFI_BUILD_MODULE=y
 CONFIG_WIFI_LOAD_DRIVER_WHEN_KERNEL_BOOTUP=y
 CONFIG_AP6XXX=y
-CONFIG_RTL8188EU=m
+CONFIG_RTL8188EU=y
 CONFIG_MWIFIEX=y
 CONFIG_MWIFIEX_SDIO=y
 CONFIG_LTE=y

添加状态指示灯

工控机包含两个led,其中一个电源指示灯,常亮,无需配置;另一个user led,需要修改dts支持。结合原理图找到对应管脚 gpio4_c2

--- a/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
@@ -152,6 +152,20 @@
                };
        };

+       leds {
+               status = "okay";
+               compatible = "gpio-leds";
+
+               user {
+                       label = "led_ctl";
+                       linux,default-trigger = "ir-user-click";
+                       gpios = <&gpio4 18 GPIO_ACTIVE_HIGH>;
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&led_user>;
+                       default-state = "on";
+               };
+       };
+
        rt5640-sound {
                compatible = "simple-audio-card";
                simple-audio-card,format = "i2s";
@@ -857,6 +871,12 @@
 };

 &pinctrl {
+       leds {
+               led_user: led-user {
+                       rockchip,pins = <4 18 RK_FUNC_GPIO &pcfg_pull_up>;
+               };
+       };
+
        buttons {
                pwrbtn: pwrbtn {
                        rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>;

使能 gpio-leds 驱动后,可以通过 /sys/class/leds/led_ctrl/brightness 文件修改亮灯状态,为了让其闪烁,可以添加开机启动脚本。

#!/bin/bash

ctl_file=/sys/class/leds/led_ctl/brightness
delay_sec=0.3

while true
do
  echo 1 > $ctl_file
  sleep $delay_sec
  echo 0 > $ctl_file
  sleep $delay_sec
done

RTC 配置

工控机外挂了 hym8563 RTC芯片,需要外接一个纽扣电池,并在 DTS 添加支持。

--- a/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts
@@ -453,6 +453,16 @@
        i2c-scl-falling-time-ns = <4>;
        clock-frequency = <400000>;

+       hym8563: hym8563@51 {
+               compatible = "haoyu,hym8563";
+               reg = <0x51>;
+               #clock-cells = <0>;
+
+               clock-frequency = <32768>;
+               /* rtc_int is connected GPIO1_C6 */
+       };
+
        vdd_cpu_b: syr837@40 {
                compatible = "silergy,syr837";
                reg = <0x40>;

添加后无法正常使用,修改时间后无法保存,最终发现是硬件问题。

PWM配置

根据原理图可知,工控机引出的 pwm 采用 pwm3a , 所以dts 中需要enable pwm3. 然后可以通过以下指令对 pwm 波进行配置。

cd /sys/class/pwm/pwmchip1
echo 0 > export
echo 50000 > pwm0/period
echo 25000 > pwm0/duty_cycle
echo 1 > pwm0/enable

其中 period 代表 pwm 波的周期,单位 ns, duty_cycle 对应占空比,以上配置为 50%.

注意,这里用的是 pwmchip1, 而不是 pwmchip0, 因为 pwmchip0 对应 pwm2, 而 pwm2vdd_log 引用,用于电源电压管理。

HDMI 无法显示

最后来看个大问题,HDMI 无法显示,首先确保hdmiroute_hdmi, display_subsystem 等相关配置已经使能。使用 Firefly 固件编译是可以正常显示的,但是 RK 固件编译却不行。

一开始想着可能是io配置问题,但是禁用了其它外设后还是不行。不过既然 Firefly 的可以,那就可以对比了,为了确认是dts问题,还是内核驱动配置问题。我将 Firefly 的dts移植到 RK 固件,修改了部分选项后编译通过,而且 HDMI 正常显示了。这说明的确是 DTS 配置问题,与内核驱动配置无关。

接下来我将 fireflydts 检查了一遍,禁用了其中的可疑选项后,HDMI又不能显示了,这说明我禁用的选项与 HDMI 显示有关,于是我将这个改动也添加到 RKdts中。

                        vcca1v8_codec: LDO_REG7 {
                                regulator-always-on;
                                regulator-boot-on;
-                               regulator-min-microvolt = <1800000>;
-                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microvolt = <900000>;
+                               regulator-max-microvolt = <900000>;
                                regulator-name = "vcca1v8_codec";
                                regulator-state-mem {
                                        regulator-off-in-suspend;

也就是 rk808 电源管理芯片中的 vcca1v8_codec 配置,将默认的 1.8v 改为 0.9v,至此,hdmi 正常显示。

总结

耗时两周左右的配置过程,让我遇到不少坑,以上总结的是其中的主要部分,还有很多小问题没有记录。总的来说,问题不少,但总算都解决了,也学到不少知识。

  1. 假定软件问题的情况下,优先排查 dts 配置,再看驱动配置
  2. 新设计的板子,可以将 gpio 配置列表整理,方便查阅
  3. 在dts中添加新设备时,可以参考当前dts外其它设备的配置信息,或者参考 Documents/devicetree/bindings/ 中的文档
  4. 在出现io输出不可控的时候,优先考虑io复用及电源域配置问题
  5. 通过查询寄存器手册,结合 io 指令可以查看或者修改寄存器配置
  6. 在确保软件配置无误情况下,如果仍旧异常,考虑硬件问题
  7. Firefly SDK 编译后始终无法启动,但 RK SDK 中使用 Fireflydts 却可以正常启动,所以优先使用 RK 官方 SDK