3. 内核源码结构解析:设备树与 SPI 关键目录

在开始设备树移植与 SPI 驱动适配之前,必须精准定位内核源码中三个核心目录:设备树源文件目录、SPI 控制器驱动目录、SPI 子系统头文件目录。本章将逐一拆解其功能、文件组织方式与协作关系。

3.1 arch/arm/boot/dts —— 设备树源文件仓库

该目录存放 ARM 架构下所有已支持的 SoC 与开发板的设备树源文件(.dts)及其包含头文件(.dtsi)。设备树是描述硬件拓扑与资源分配的数据结构,内核在启动时通过 Flattened Device Tree (FDT) 解析硬件信息。

3.1.1 文件命名与层级规则

文件类型 命名示例 作用
SoC 级 .dtsi imx6ull.dtsi 定义 SoC 内部所有外设节点(SPI、I2C、UART、时钟等),不包含板级差异
板级 .dts imx6ull-14x14-evk.dts 通过 #include 引用 SoC .dtsi,并添加板级外设、GPIO 复用、引脚配置
头文件 .h imx6ull-pinfunc.h 定义引脚复用宏(如 MX6UL_PAD_UART1_TX__GPIO1_IO16),供 .dts 引用
移植要点: 当为新板卡添加支持时,优先查找最接近的 SoC .dtsi 文件,然后新建板级 .dts 并 #include 该 .dtsi,避免重复定义 SoC 级节点。

3.1.2 设备树节点结构示例(SPI 控制器)

/* imx6ull.dtsi 中的 SPI 控制器节点 */
spi1: spi@02010000 {
    compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
    reg = <0x02010000 0x4000>;
    interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_ECSPI1>,
             <&clks IMX6UL_CLK_ECSPI1>;
    clock-names = "ipg", "per";
    dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
    dma-names = "rx", "tx";
    status = "disabled";
    #address-cells = <1>;
    #size-cells = <0>;
};
关键字段说明:
  • compatible:驱动匹配的核心字符串,必须与 SPI 控制器驱动中的 of_match_table 一致。
  • reg:寄存器基地址与长度,驱动通过 platform_get_resource() 获取。
  • status = "disabled":默认关闭,板级 .dts 中需显式设置为 "okay" 并添加引脚复用。

3.1.3 设备树文件层级关系图

设备树文件层级关系 imx6ull.dtsi SoC 级外设定义 imx6ull-14x14-evk.dts 板级外设与引脚配置 imx6ull-pinfunc.h 引脚复用宏定义 #include #include imx6ull-14x14-evk.dtb dtc 编译 ⚠ 移植流程: 1. 复制最接近的板级 .dts → 2. 修改 compatible 与 model → 3. 使能 SPI 节点并配置 pinctrl → 4. 编译生成 .dtb

3.2 drivers/spi —— SPI 控制器驱动实现

该目录包含所有 SPI 控制器(SPI Master)的驱动代码,以及 SPI 子系统核心层(spi.c)与通用传输接口。每个 SPI 控制器驱动通常对应一个 .c 文件,命名规则为 spi-<controller>.c。

3.2.1 目录文件分类

文件/目录 功能 典型示例
spi.c SPI 子系统核心:总线注册、设备与驱动匹配、传输队列管理 —
spi-mem.c SPI 内存协议层(SPINOR/SPINAND 共用) —
spi-imx.c i.MX 系列 ECSPI 控制器驱动 i.MX6ULL / i.MX8M
spi-bcm2835.c BCM2835(Raspberry Pi)SPI 控制器驱动 Raspberry Pi 3/4
spi-rockchip.c Rockchip SPI 控制器驱动 RK3399 / RK3568

3.2.2 驱动与设备树的匹配机制

SPI 控制器驱动通过 of_match_table 与设备树节点中的 compatible 属性进行匹配。以下为 spi-imx.c 中的匹配表示例:

static const struct of_device_id spi_imx_dt_ids[] = {
    { .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, },
    { .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, },
    { .compatible = "fsl,imx27-cspi", .data = &imx27_cspi_devtype_data, },
    { .compatible = "fsl,imx31-cspi", .data = &imx31_cspi_devtype_data, },
    { .compatible = "fsl,imx35-cspi", .data = &imx35_cspi_devtype_data, },
    { .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
    { .compatible = "fsl,imx6ul-ecspi", .data = &imx6ul_ecspi_devtype_data, },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
常见错误: 设备树中 compatible 字符串与驱动表中的字符串必须完全一致(包括大小写与逗号后空格)。例如 "fsl,imx6ul-ecspi" 不能写成 "fsl,imx6ul-ecspi "(尾部空格)或 "fsl,imx6ul-ecspi1"。

3.2.3 SPI 控制器驱动注册流程

SPI 控制器驱动注册流程 module_platform_driver() probe() 回调 spi_register_master() 匹配成功 注册 Master probe() 内部典型步骤 ① 获取设备树资源:of_iomap() / platform_get_resource() ② 获取时钟与中断:clk_get() / devm_request_irq() ③ 初始化 SPI 控制器硬件:设置波特率、模式、DMA ④ 调用 spi_register_master() 将控制器注册到 SPI 核心

3.3 include/linux/spi —— SPI 子系统头文件

该目录定义了 SPI 子系统对外暴露的数据结构、接口函数与宏。无论是 SPI 控制器驱动还是 SPI 设备驱动,都需要引用这些头文件。

3.3.1 核心头文件清单

头文件 核心定义 使用场景
spi.h struct spi_master、struct spi_device、struct spi_transfer、struct spi_message 所有 SPI 驱动必须包含
spi-mem.h struct spi_mem、struct spi_mem_op、SPI 内存操作接口 SPI NOR / NAND 驱动
spi-gpio.h GPIO 模拟 SPI 的板级配置结构 软件 SPI 位操作驱动

3.3.2 关键数据结构关系

SPI 核心数据结构关系 struct spi_master 控制器抽象 struct spi_device 从设备抽象 struct spi_message 传输消息 struct spi_transfer 单次传输段 管理 发起 包含多个 传输流程: spi_message_init() → spi_message_add_tail() → spi_sync()

3.4 三个目录的协作关系

设备树、SPI 控制器驱动、SPI 子系统头文件三者构成完整的 SPI 硬件支持链路:

  1. 设备树(arch/arm/boot/dts)描述 SPI 控制器的寄存器地址、中断、时钟、引脚复用。
  2. SPI 控制器驱动(drivers/spi/spi-*.c)解析设备树节点,初始化硬件,并通过 include/linux/spi/spi.h 中的接口注册到 SPI 核心。
  3. SPI 核心(spi.c + spi.h)管理总线、设备枚举、传输队列,向上层提供统一的 spi_write_then_read() 等 API。
实战建议: 在移植 SPI 驱动时,应同时打开以下三个目录的终端窗口:
arch/arm/boot/dts/ —— 修改设备树
drivers/spi/ —— 调试控制器驱动
include/linux/spi/ —— 查阅数据结构定义