用时两周,总算将新的 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| [ 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不匹配。原理图中配置如下:

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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
@@ -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 去使能。
1 2 3 4 5 6 7 8 9 10 11 12
| #!/bin/bash cd /sys/class/gpio
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 将用于查找基地址,offset 0x00014 是 PMUGRF_GPIO1B_IOMUX 的偏移量。
那么如何查找基地址呢?其实文档的第一张图就是地址映射图 Address Mapping。从中可以找到 PMUGRF的基地址,当然也可以找到其它寄存器的基地址。

从图中知道基地址为 0xFF320000, 加上偏移量 0x00014 就等于 0xFF320014.
最后通过 io 指令去查询。
1 2 3 4 5 6 7
|
root@firefly:~# io -4 -r 0xff320014 ff320014: 00008141
|
查询结果是 0x00008141, 这个值该怎么读呢,这个是32位数据,对应gpio1_b0~gpio1_b7 共8个gpio的复用情况。每两个bit代表一个gpio。详情查看TRM文档中 PMUGRF_GPIO1B_IOMUX 寄存器说明。

对应 0x00008141, 可以解析出每个gpio的复用情况。
最终查询到USB 3.0 gpio1_b1, gpio1_b2 , gpio1_b5 均为 GPIO 模式,没有被复用,说明配置正常。
查询USB gpio value
既然复用寄存器没有问题,那再来看看 gpio value配置,看看使用 sys/class/gpio/gpioxx/value 配置有没有生效。
与查询复用情况类似,首先找到 gpio1的基地址。

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

DR 寄存器偏移量为 0x0000, 32位寄存器对应gpio1 32个gpio的数据。接下来使用 io 指令查询。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
root@firefly:~# io -4 -r 0xff730000 ff730000: 00042602
root@firefly:~# io -4 -r 0xff730004 ff730004: 00062602
|
以上查询到 DR 和 DDR 的配置情况,均正常。
USB 3.0 gpio 电源域配置
到此为止,io配置、io查询、复用功能确认都正常,但是实际输出就是不对。最后只能在 RK redmine 系统中提交issues,根据他们提供的技术文档,发现io输出不受控的问题可能与电源域配置有关。
首先查看原理图,gpio1 的电源接口。

gpio1 使用 PMUIO2 电源,电压1.8v。然后查看 dts 中的配置。发现是 3.0v, 果然不一样!
1 2
| uboot-set = <RK3399_PMU1830_VDD_3V0>; pmu1830-supply = <&vcc_3v0>;
|
既然问题找到了,那解决方案就很明显了。
解决方案
修改 pmu1830 电源域配置。修改后,USB3.0 供电一切正常。
1 2 3 4 5 6 7 8 9 10 11 12 13
| --- 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 配置与原理图中是不匹配的,需要更正一下。
1 2 3 4 5 6 7 8 9 10
| --- 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失败。
1 2 3
| 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 排查。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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
1 2 3
| 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 正常执行。
1 2 3 4
| brctl addbr br0 brctl addif br0 eth0 brctl addif br0 eth1 ifconfig br0 192.168.64.20 up
|
添加 USB wifi 支持
由于工控机不自带wifi模块,可能会用到usb wifi网卡,所以需要添加驱动支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| --- 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_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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| --- 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 文件修改亮灯状态,为了让其闪烁,可以添加开机启动脚本。
1 2 3 4 5 6 7 8 9 10 11 12
| #!/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 添加支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| --- 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-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 波进行配置。
1 2 3 4 5
| 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, 而 pwm2 被 vdd_log 引用,用于电源电压管理。
HDMI 无法显示
最后来看个大问题,HDMI 无法显示,首先确保hdmi , route_hdmi, display_subsystem 等相关配置已经使能。使用 Firefly 固件编译是可以正常显示的,但是 RK 固件编译却不行。
一开始想着可能是io配置问题,但是禁用了其它外设后还是不行。不过既然 Firefly 的可以,那就可以对比了,为了确认是dts问题,还是内核驱动配置问题。我将 Firefly 的dts移植到 RK 固件,修改了部分选项后编译通过,而且 HDMI 正常显示了。这说明的确是 DTS 配置问题,与内核驱动配置无关。
接下来我将 firefly 的 dts 检查了一遍,禁用了其中的可疑选项后,HDMI又不能显示了,这说明我禁用的选项与 HDMI 显示有关,于是我将这个改动也添加到 RK 的 dts中。
1 2 3 4 5 6 7 8 9 10
| 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 正常显示。
总结
耗时两周左右的配置过程,让我遇到不少坑,以上总结的是其中的主要部分,还有很多小问题没有记录。总的来说,问题不少,但总算都解决了,也学到不少知识。
- 假定软件问题的情况下,优先排查
dts 配置,再看驱动配置
- 新设计的板子,可以将
gpio 配置列表整理,方便查阅
- 在dts中添加新设备时,可以参考当前dts外其它设备的配置信息,或者参考
Documents/devicetree/bindings/ 中的文档
- 在出现io输出不可控的时候,优先考虑io复用及电源域配置问题
- 通过查询寄存器手册,结合
io 指令可以查看或者修改寄存器配置
- 在确保软件配置无误情况下,如果仍旧异常,考虑硬件问题
-
Firefly SDK 编译后始终无法启动,但 RK SDK 中使用 Firefly的 dts 却可以正常启动,所以优先使用 RK 官方 SDK