第4章:Linux内核移植——从源码到设备树
内核移植,说白了就是让Linux内核认识你的硬件板子。很多新手一上来就急着改代码,结果连内核怎么编译的都没搞明白。我个人习惯,先花半天把整个流程走通,再回头抠细节。
4.1 内核源码获取与配置
瑞芯微官方提供了完整的BSP包,里面包含了内核源码、交叉编译工具链、U-Boot等。你可以在Rockchip的GitHub仓库或者百度网盘上找到对应芯片的源码包。
rk-5.10 或 rk-6.1,这些分支已经集成了大量平台补丁。
拿到源码后,第一步是配置内核。RK平台通常提供三类配置方式:
| 配置方式 | 命令示例 | 适用场景 |
|---|---|---|
| 默认配置 | make ARCH=arm64 rockchip_linux_defconfig |
快速验证,适合首次编译 |
| 菜单配置 | make ARCH=arm64 menuconfig |
按需裁剪驱动和功能 |
| 碎片配置 | make ARCH=arm64 savedefconfig |
生成最小化配置文件 |
我在项目中遇到过一个问题:用 menuconfig 改完配置后,下次编译时发现改动没生效。后来才意识到,menuconfig 改的是 .config 文件,而 rockchip_linux_defconfig 是默认配置。如果你改了 .config,下次执行 make defconfig 会被覆盖掉。正确的做法是:
# 先加载默认配置
make ARCH=arm64 rockchip_linux_defconfig
# 再手动调整
make ARCH=arm64 menuconfig
# 保存为新的defconfig
make ARCH=arm64 savedefconfig
cp defconfig arch/arm64/configs/my_board_defconfig
CROSS_COMPILE 环境变量。我曾经忘了加,结果编译出来的内核在板子上跑不起来,排查了半天才发现是用了本机的gcc。
4.2 设备树(DTS)基础与语法
设备树,说白了就是描述硬件信息的配置文件。内核通过它来知道你的板子上有哪些外设、它们挂在哪里、怎么初始化。没有设备树,内核就是个瞎子。
DTS的语法其实很简单,核心就三个概念:节点、属性和值。来看一个典型的RK3588设备树片段:
/dts-v1/;
#include "rk3588.dtsi"
/ {
model = "Rockchip RK3588 EVB1 Board";
compatible = "rockchip,rk3588-evb1", "rockchip,rk3588";
chosen {
stdout-path = &uart2;
};
memory@a00000 {
device_type = "memory";
reg = <0x0 0xa00000 0x0 0x80000000>;
};
};
&uart2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart2m0_xfer>;
};
这里有几个关键点:
/dts-v1/;:版本声明,必须放在文件开头#include:可以像C语言一样包含头文件,.dtsi是SoC级描述,.dts是板级描述&uart2:引用标签,用来覆盖或追加已有节点的属性status = "okay":使能某个外设,默认是"disabled"
核心原则: SoC级通用描述放 .dtsi,板级差异放 .dts。这样换板子时只需要改 .dts,不用动SoC的公共部分。
嗯,这里要注意一个坑:设备树的 reg 属性,地址和大小是成对出现的。比如 reg = <0x0 0xa00000 0x0 0x80000000>,表示起始地址 0xa00000,大小 0x80000000(2GB)。如果你写错了地址范围,内核可能连内存都认不全。
4.3 RK平台DTS文件修改实战
光说不练假把式。我们拿一个实际场景来走一遍:给RK3588的板子添加一个SPI设备。
首先,找到板级DTS文件。RK3588的板级DTS通常在 arch/arm64/boot/dts/rockchip/ 目录下,比如 rk3588-evb1.dts。打开它,你会看到它已经 #include 了 rk3588.dtsi。
接下来,在板级DTS中使能SPI控制器:
&spi2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi2m2_cs0 &spi2m2_pins>;
max-freq = <50000000>;
spidev@0 {
compatible = "rockchip,spidev";
reg = <0>;
spi-max-frequency = <25000000>;
};
};
这里我踩过一个坑:pinctrl 的引脚配置必须和原理图对得上。RK3588的SPI2有多个复用选项,比如 spi2m0、spi2m1、spi2m2,你得查芯片手册确认你的板子用的是哪一组引脚。我曾经因为选错了复用组,SPI死活不通,最后拿示波器量才发现CLK根本没输出。
修改完DTS后,编译生成DTB:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs
生成的DTB文件在 arch/arm64/boot/dts/rockchip/rk3588-evb1.dtb。把它烧录到板子的 boot 分区,重启后就能在 /dev/spidev2.0 看到设备节点了。
dmesg | grep spi。常见错误是引脚冲突或者时钟配置不对。我一般会在DTS里加上 status = "okay" 后,先编译一个最小系统验证,避免被其他驱动干扰。
最后,我们来看一下整个内核移植过程中,设备树扮演的角色。下面这张图能帮你理清思路:
你看,整个流程其实不复杂。内核源码和DTS是输入,交叉编译工具链是加工工具,最终产出内核镜像和DTB。U-Boot启动时把这两个文件加载到内存,内核解析DTB后就知道怎么初始化硬件了。
嗯,关于内核移植和设备树,今天就先聊这么多。记住一句话:DTS改得好,移植没烦恼。多花点时间把设备树写对,后面驱动开发会省很多事。