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里没有现成的选项,怎么办?自己加。

步骤很简单,分三步走:

  1. 在Kconfig里声明选项
  2. 在代码里使用选项
  3. 在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 配置系统的整体流程

说了这么多,我画张图帮你理清整个配置系统的脉络:

U-Boot配置系统流程图 U-Boot配置系统流程图 defconfig 文件 make xxx_defconfig .config 文件 make menuconfig make 编译 u-boot.bin / u-boot.itb 提示:savedefconfig 可将 .config 精简后回写到 defconfig,保持配置整洁

从图上你能看到: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 往上查,很快能找到问题。