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 与最终编译产物之间的关系:
3.7 实战:如何快速定位一个驱动
假设你要找 RK3588 的 HDMI 驱动,怎么找?
- 先看设备树:打开
rk3588.dtsi,搜索hdmi,找到 compatible 属性,比如rockchip,rk3588-hdmi - 搜索 compatible:在源码根目录执行
grep -r "rockchip,rk3588-hdmi" drivers/ - 定位驱动文件:大概率在
drivers/gpu/drm/rockchip/下,找到rk3588_hdmi.c - 检查 Kconfig:看这个驱动对应的配置选项是什么,确保
make menuconfig里选上了
核心思路: 设备树的 compatible 是驱动和硬件的「桥梁」。你只要在 dts 里看到 compatible,就能在驱动代码里找到对应的 of_match_table。这是内核移植最常用的调试手段。