3. 内核源码结构分析:Linux Rockchip 内核目录树详解

拿到一份 Rockchip 的内核源码,很多人第一反应是直接 make menuconfig 然后开编。嗯,我早期也这么干过,结果编译倒是过了,板子死活起不来。后来花了整整两天翻目录,才搞清楚问题出在设备树文件没放对位置。

说白了,搞内核移植,你得先知道「什么东西在哪儿」。这一章我就带你捋一遍 Linux 内核的目录树,重点标注 Rockchip 平台相关的代码位置,顺便把 Kconfig 和 Makefile 的套路讲透。

3.1 Linux 内核顶层目录结构

先看顶层。你解压内核源码后,第一眼看到的目录大概有 20 多个。我挑几个跟移植强相关的说说:

目录 作用 移植关注度
arch/ 体系结构相关代码,ARM64 就在这里 ⭐⭐⭐⭐⭐
drivers/ 设备驱动,Rockchip 的 DRM、I2C、SPI 都在里面 ⭐⭐⭐⭐⭐
include/ 头文件,设备树绑定头文件在 dt-bindings/ ⭐⭐⭐⭐
Documentation/ 内核文档,设备树绑定说明在这里 ⭐⭐⭐
scripts/ 编译脚本、设备树编译工具 ⭐⭐
sound/ 音频子系统,Rockchip 的 I2S/HDMI 音频驱动 ⭐⭐⭐

我个人习惯,拿到一个新 BSP 包,第一件事就是看 arch/arm64/boot/dts/rockchip/ 目录。为什么?因为设备树文件直接决定了你的板级配置对不对。

3.2 Rockchip 平台代码位置

Rockchip 的代码分散在内核的几个关键位置。我整理了一张表,方便你快速定位:

功能模块 代码路径 说明
SoC 核心代码 arch/arm64/mach-rockchip/ 包含 SMP、PSCI、IOMMU 等底层初始化
设备树文件 arch/arm64/boot/dts/rockchip/ rk3399.dtsi、rk3588.dtsi 等 SoC 级文件
时钟驱动 drivers/clk/rockchip/ clk-rk3399.c、clk-rk3588.c 等
电源管理 drivers/soc/rockchip/ pm_domains、rockchip_system_monitor
显示驱动 drivers/gpu/drm/rockchip/ VOP、HDMI、DP、MIPI DSI 驱动
多媒体 drivers/media/platform/rockchip/ ISP、MIPI CSI、V4L2 驱动
音频 sound/soc/rockchip/ I2S、SPDIF、HDMI 音频
PCIe/USB drivers/pci/controller/dwc/ pcie-rockchip.c、pcie-dwc-rockchip.c
我的经验: 如果你要移植一个新板子,先复制一份最接近的 .dts 文件,然后对照原理图修改。我曾经在 RK3399 上移植时,直接拿 EVB 的 dts 改,省了至少两天时间。

3.3 Kconfig 与 Makefile 机制

Kconfig 和 Makefile 是内核配置系统的核心。你想想看,没有它们,你怎么控制哪些代码编译进内核?

3.4 Kconfig 语法速览

Kconfig 文件定义了配置选项。以 Rockchip 的时钟驱动为例:

# drivers/clk/rockchip/Kconfig
config CLK_ROCKCHIP
    bool "Rockchip clock controller support"
    depends on ARCH_ROCKCHIP
    help
      Support for clock controllers on Rockchip SoCs.

config CLK_RK3399
    bool "Rockchip RK3399 clock controller"
    depends on CLK_ROCKCHIP
    help
      Clock controller for the Rockchip RK3399 SoC.

这里有几个关键点:

  • depends on:依赖关系,RK3399 的时钟驱动依赖 CLK_ROCKCHIP 被选中
  • bool/tristate:bool 表示只能编译进内核,tristate 可以编译成模块
  • help:帮助信息,写清楚这个选项是干什么的
避坑: 我曾经在 RK3588 上遇到过一个问题——明明在 menuconfig 里选了时钟驱动,编译却报找不到符号。查了半天发现是 depends on 链断了,父选项没选上。所以配置时一定要检查依赖链。

3.5 Makefile 编译规则

Makefile 告诉内核怎么编译这些文件。还是以时钟驱动为例:

# drivers/clk/rockchip/Makefile
obj-$(CONFIG_CLK_ROCKCHIP) += clk-rockchip.o
obj-$(CONFIG_CLK_RK3399)   += clk-rk3399.o
obj-$(CONFIG_CLK_RK3588)   += clk-rk3588.o

这里的 obj-$(CONFIG_XXX) 是核心语法:

  • 如果 CONFIG_CLK_RK3399=y,则 clk-rk3399.o 会被编译进内核
  • 如果 CONFIG_CLK_RK3399=m,则编译成模块 clk-rk3399.ko
  • 如果没定义,则不编译

我个人习惯在调试新驱动时,先用 =m 编译成模块,这样可以在板子上动态加载/卸载,不用每次都烧写内核。

3.6 知识体系结构图

下面这张图展示了内核源码、Kconfig/Makefile 与最终编译产物之间的关系:

内核源码编译流程与 Rockchip 代码位置 内核源码编译流程与 Rockchip 代码位置 内核源码树 arch/arm64/boot/dts/rockchip/ drivers/clk/rockchip/ drivers/gpu/drm/rockchip/ Kconfig / Makefile depends on / select obj-$(CONFIG_XXX) 编译产物 zImage / Image .dtb 设备树 .ko 内核模块 核心流程:源码 → Kconfig/Makefile 配置 → 编译生成 Image、dtb、ko Rockchip 代码分散在 arch/、drivers/、sound/ 等目录,通过 Kconfig 统一管理

3.7 实战:如何快速定位一个驱动

假设你要找 RK3588 的 HDMI 驱动,怎么找?

  1. 先看设备树:打开 rk3588.dtsi,搜索 hdmi,找到 compatible 属性,比如 rockchip,rk3588-hdmi
  2. 搜索 compatible:在源码根目录执行 grep -r "rockchip,rk3588-hdmi" drivers/
  3. 定位驱动文件:大概率在 drivers/gpu/drm/rockchip/ 下,找到 rk3588_hdmi.c
  4. 检查 Kconfig:看这个驱动对应的配置选项是什么,确保 make menuconfig 里选上了
核心思路: 设备树的 compatible 是驱动和硬件的「桥梁」。你只要在 dts 里看到 compatible,就能在驱动代码里找到对应的 of_match_table。这是内核移植最常用的调试手段。

3.8 本章小结