4. 配置系统与Kconfig:Rockchip平台的defconfig文件详解
做U-Boot移植,说白了就是跟配置系统打交道。你想想看,一块开发板上有DDR、SD卡、网络、显示……这么多外设,U-Boot怎么知道该初始化哪些?答案就在配置系统里。
Rockchip平台用的是Kconfig + defconfig这套机制。我刚开始接触时也觉得有点绕,但摸清楚规律后,你会发现它其实很清晰。
4.1 defconfig文件长什么样?
先找个实际的例子看看。以RK3568 EVB板为例,它的defconfig文件在 configs/evb_rk3568_defconfig。我截取一段核心内容:
CONFIG_ARM=y
CONFIG_ARCH_ROCKCHIP=y
CONFIG_SYS_TEXT_BASE=0x00a00000
CONFIG_NR_DRAM_BANKS=2
CONFIG_ENV_SIZE=0x8000
CONFIG_ENV_OFFSET=0x3F8000
CONFIG_DEFAULT_DEVICE_TREE="rk3568-evb"
CONFIG_ROCKCHIP_RK3568=y
CONFIG_TARGET_EVB_RK3568=y
CONFIG_SPL_FIT_GENERATOR="arch/arm/mach-rockchip/make_fit_atf.py"
CONFIG_MISC_INIT_R=y
CONFIG_BOOTDELAY=2
CONFIG_USE_PREBOOT=y
CONFIG_PREBOOT="pci enum;"
看到没?每一行都是一个配置选项。以 CONFIG_ 开头,后面跟选项名和值。值可以是 y(启用)、n(禁用),也可以是字符串或数字。
核心要点:defconfig文件就是U-Boot的"配置清单"。它告诉构建系统:我要编译哪些模块、用哪个设备树、DDR怎么配、环境变量放哪里。
4.2 配置选项的分类与作用
我把常见的配置选项分成了几类,这样好理解:
| 类别 | 示例选项 | 作用 |
|---|---|---|
| 架构选择 | CONFIG_ARM=y |
指定CPU架构 |
| SoC选择 | CONFIG_ROCKCHIP_RK3568=y |
指定具体芯片型号 |
| 板级选择 | CONFIG_TARGET_EVB_RK3568=y |
指定开发板型号 |
| 内存配置 | CONFIG_NR_DRAM_BANKS=2 |
DDR通道数 |
| 环境变量 | CONFIG_ENV_OFFSET=0x3F8000 |
env存储位置 |
| 设备树 | CONFIG_DEFAULT_DEVICE_TREE="rk3568-evb" |
默认设备树文件 |
| 功能开关 | CONFIG_MISC_INIT_R=y |
板级初始化函数 |
我个人习惯是先看 CONFIG_ARCH_ROCKCHIP 和 CONFIG_ROCKCHIP_RK3568 这两个,它们决定了整个编译流程走哪条路。
4.3 menuconfig图形化配置
光看文本文件不过瘾?那就用 menuconfig。在U-Boot根目录执行:
make evb_rk3568_defconfig
make menuconfig
你会看到一个蓝底白字的图形界面。这里我分享一个经验:别在menuconfig里直接改配置。为什么?因为menuconfig改完后,它只修改了 .config 文件,不会回写到 defconfig。下次 make distclean 就全丢了。
我的做法:先用menuconfig浏览和调试,确认配置正确后,执行 make savedefconfig,它会生成一个精简版的 defconfig,再手动覆盖原文件。
menuconfig里怎么找选项?按 / 键搜索。比如搜 ROCKCHIP_RK3568,它会告诉你这个选项在哪个菜单路径下、依赖什么、被谁选择。
4.4 Kconfig的依赖关系
这里有个坑,我踩过好几次。Kconfig选项之间有依赖关系,不是你想选就能选的。看个例子:
config ROCKCHIP_RK3568
bool "Support Rockchip RK3568"
select ARM
select SPL
select SPL_FIT
select SPL_LOAD_FIT
select SPL_OF_CONTROL
select SPL_DM
select SPL_SERIAL
select SPL_DRIVERS_MISC
select SUPPORT_SPL
select SUPPORT_TPL
看到 select 关键字了吗?当你选择 ROCKCHIP_RK3568 时,它会自动选中后面那一串选项。这叫"反向依赖"。
还有一种叫"正向依赖":
config SPL_DM
bool "Enable Driver Model in SPL"
depends on DM
意思是:你想用 SPL_DM,必须先打开 DM。如果 DM 没开,SPL_DM 在menuconfig里是灰色的,选不了。
避坑指南:我曾经在移植一块RK3588板子时,SPL死活起不来。查了两天,最后发现是 SPL_FIT_GENERATOR 没配对。这个选项依赖 SPL_FIT,而 SPL_FIT 又依赖 SPL_LOAD_FIT……一层层追下去才找到根因。所以,遇到配置不生效,先检查依赖链。
4.5 如何添加自定义配置选项
假设你要给一块新板子加个特殊功能——比如板载的FPGA初始化。U-Boot里没有现成的选项,怎么办?自己加。
步骤很简单,分三步走:
- 在Kconfig里声明选项
- 在代码里使用选项
- 在defconfig里启用选项
先看第一步。找到板级目录下的Kconfig文件,比如 board/rockchip/evb_rk3568/Kconfig:
if TARGET_EVB_RK3568
config EVB_RK3568_FPGA_INIT
bool "Enable FPGA initialization on EVB RK3568"
default n
help
Say Y here to enable FPGA initialization sequence
during board boot. This will configure the FPGA
via SPI interface.
config EVB_RK3568_FPGA_SPI_BUS
int "SPI bus number for FPGA"
default 0
depends on EVB_RK3568_FPGA_INIT
endif
第二步,在板级初始化代码里使用:
#include <common.h>
int board_init(void)
{
/* 其他初始化代码... */
#ifdef CONFIG_EVB_RK3568_FPGA_INIT
printf("Initializing FPGA on SPI bus %d...\n",
CONFIG_EVB_RK3568_FPGA_SPI_BUS);
/* 调用FPGA初始化函数 */
fpga_init(CONFIG_EVB_RK3568_FPGA_SPI_BUS);
#endif
return 0;
}
第三步,在defconfig里加上:
CONFIG_EVB_RK3568_FPGA_INIT=y
CONFIG_EVB_RK3568_FPGA_SPI_BUS=1
搞定。重新编译,你的FPGA初始化代码就进去了。
4.6 配置系统的整体流程
说了这么多,我画张图帮你理清整个配置系统的脉络:
从图上你能看到:defconfig 是源头,经过 make xxx_defconfig 生成 .config。然后两条路——要么直接编译,要么进 menuconfig 微调后再编译。最终生成可烧录的二进制文件。
4.7 实战建议
最后,给几个我实际干活时总结的经验:
- 别手写defconfig:从已有的相近板子复制一份,再改。比如做RK3568新板,就拿
evb_rk3568_defconfig当模板。 - 善用
make savedefconfig:它会把.config里非默认值的选项提取出来,生成一个干净的defconfig。我每次调完配置都跑一遍这个。 - 版本管理要跟上:defconfig文件一定要进git。我见过同事改了配置没提交,结果别人编译出来的固件功能对不上,排查了半天。
- Kconfig里加注释:自定义选项的
help字段别偷懒。三个月后你自己回来看,绝对想不起来当初为什么加这个选项。
小技巧:调试配置问题时,可以在 .config 文件里搜 CONFIG_ 开头的选项,看它是不是 =y。如果明明选了但没生效,八成是依赖链断了——顺着Kconfig的 depends on 和 select 往上查,很快能找到问题。