4. RISC-V Linux内核源码结构
在RISC-V平台上做驱动适配,第一步不是写代码,而是看懂Linux内核源码的“地图”。内核源码并不是一堆随意堆放的C文件,它按功能、架构和构建规则被严格组织。理解这套组织方式,才能快速定位RISC-V相关代码、找到驱动子系统入口,并正确配置交叉编译工具链。
一、内核目录组织:从顶层看功能分区
Linux内核源码根目录下,每个一级目录都承担明确职责。对RISC-V驱动适配而言,最常打交道的目录如下:
arch/:架构相关代码,RISC-V对应arch/riscv/,包含启动、异常、MMU、设备树、SBI调用等。drivers/:驱动子系统,按总线或功能分类,如drivers/gpio/、drivers/i2c/、drivers/net/。include/:公共头文件,include/linux/为通用接口,include/asm-riscv/链接到架构头文件。kernel/:核心调度、中断、时间、同步等与架构无关的内核逻辑。mm/:内存管理,RISC-V的页表、TLB、内存模型相关代码会与arch/riscv/mm/协作。lib/:通用库函数,如字符串、CRC、位操作等。scripts/:构建脚本、Kconfig解析、设备树编译工具等。
驱动工程师最需要建立的条件反射是:看到外设类型,先想它属于哪个子系统;看到架构差异,先查arch/riscv/;看到编译开关,先找对应Kconfig和Makefile。
二、架构相关代码:RISC-V的“专属层”
arch/riscv/是RISC-V平台适配的核心。它主要包含:
boot/:内核启动入口、设备树处理、早期控制台。kernel/:异常向量、SBI调用、CPU热插拔、信号处理。mm/:页表初始化、TLB刷新、内存映射。include/asm/:架构相关宏、寄存器定义、屏障指令。dts/:RISC-V开发板设备树源文件。
驱动适配时,架构层通常不直接改,但必须理解它提供的接口。例如,设备树中的compatible字符串会与驱动匹配;中断号通过irqchip和PLIC映射;DMA操作依赖dma-mapping架构实现。
ecall进入M模式,驱动中一般不需要直接调用SBI,但排障时要能识别SBI相关日志。三、驱动子系统:按总线与功能分层
Linux驱动模型围绕总线、设备、驱动三者匹配展开。RISC-V平台常见驱动子系统包括:
| 子系统 | 目录 | 典型设备 | 关键接口 |
|---|---|---|---|
| GPIO | drivers/gpio/ | GPIO控制器 | gpiod_get、gpiod_direction_output |
| I2C | drivers/i2c/ | 传感器、EEPROM | i2c_transfer、i2c_smbus_read_byte |
| SPI | drivers/spi/ | Flash、显示屏 | spi_sync、spi_write |
| 网络 | drivers/net/ | 以太网MAC | netdev_ops、phy_connect |
| 字符设备 | drivers/char/ | 串口、自定义设备 | file_operations、cdev_add |
每个子系统内部通常分为“核心层”和“控制器驱动”。核心层提供统一API,控制器驱动负责具体硬件。以GPIO为例,gpiolib.c是核心,gpio-riscv.c或厂商GPIO驱动负责注册gpio_chip。
四、编译配置与交叉编译工具链
内核构建由Kconfig和Makefile共同驱动。Kconfig定义配置项,Makefile决定编译哪些文件。RISC-V交叉编译需要指定工具链前缀和架构:
export ARCH=riscv
export CROSS_COMPILE=riscv64-linux-gnu-
make defconfig
make menuconfig
make -j$(nproc) Image.gz dtbs modules
常用配置项包括:
CONFIG_ARCH_RISCV=y:启用RISC-V架构支持。CONFIG_SOC_VIRT=y或厂商SoC选项:选择具体平台。CONFIG_GPIO_SYSFS=y:启用GPIO用户空间接口,便于调试。CONFIG_I2C_CHARDEV=y:启用I2C字符设备,方便用户态访问。
五、案例:为RISC-V虚拟平台添加一个GPIO驱动
假设在QEMU RISC-V virt平台上添加一个简单GPIO控制器驱动。流程如下:
- 在
drivers/gpio/下新建gpio-demo.c,实现gpio_chip的direction_output和get。 - 在
drivers/gpio/Kconfig中添加config GPIO_DEMO,依赖ARCH_RISCV。 - 在
drivers/gpio/Makefile中添加obj-$(CONFIG_GPIO_DEMO) += gpio-demo.o。 - 在设备树中增加节点,
compatible = "demo,gpio",并指定寄存器基址和中断。 - 执行
make menuconfig选中GPIO_DEMO,重新编译内核和设备树。
启动后可通过/sys/class/gpio或gpiodetect验证驱动是否加载。若匹配失败,优先检查compatible字符串、Kconfig依赖和模块是否被编译进内核。
六、误区与复盘
常见误区有三个:一是把架构代码和驱动代码混在一起改,导致升级内核时冲突;二是只改Makefile不改Kconfig,配置项无法被选中;三是忽略设备树,驱动写好了但设备没有注册。复盘时应建立“配置—编译—设备树—驱动匹配—用户接口”的完整链路检查表。
掌握这张“源码地图”后,RISC-V驱动适配就不再是盲目搜索。先定位子系统,再确认架构接口,接着配置Kconfig和Makefile,最后用设备树完成匹配,整个链路清晰可控。