3. SPI控制器节点配置

SPI控制器节点是设备树中描述SPI主机硬件能力的核心节点。它向内核传递控制器的寄存器基址、中断号、时钟频率、DMA通道等关键资源信息,是后续从设备枚举与通信的基础。本节详细解析SPI控制器节点的三大核心属性:compatible、reg、interrupts,并辅以完整节点示例。

3.1 SPI控制器节点属性总览

一个典型的SPI控制器节点通常包含以下属性,其中前三个为必选属性:

属性名 类型 必选 说明
compatible 字符串列表 是 匹配驱动与硬件,格式为"厂商,型号"
reg 地址/长度对 是 控制器寄存器基址与映射范围
interrupts 中断描述 是 控制器中断号与触发类型
clocks phandle列表 推荐 SPI模块时钟与PCLK时钟
dmas phandle+参数 可选 DMA传输通道描述
#address-cells u32 是 子节点地址宽度,SPI总线通常为1
#size-cells u32 是 子节点地址长度,SPI总线通常为0
关键理解: #address-cells = <1> 和 #size-cells = <0> 是SPI总线节点的固定搭配,表示每个从设备使用一个32位整数作为片选编号,且不占用地址空间范围。

3.2 compatible属性:驱动匹配的钥匙

compatible 属性是设备树与驱动代码之间的绑定契约。内核通过该属性查找已注册的 platform_driver 或 spi_controller_driver,完成硬件与驱动的匹配。

属性值采用 "厂商,型号" 格式,厂商名使用小写字母,型号与数据手册一致。推荐同时提供兼容性回退字符串:

compatible = "fsl,imx6ul-ecspi", "fsl,imx6q-ecspi";

匹配优先级:内核先尝试第一个字符串 fsl,imx6ul-ecspi,若未找到驱动则尝试第二个 fsl,imx6q-ecspi。这种设计允许同一驱动兼容多个硬件版本。

SoC平台 compatible字符串 对应驱动文件
i.MX6UL fsl,imx6ul-ecspi spi-imx.c
STM32MP1 st,stm32h7-spi spi-stm32.c
Zynq-7000 xlnx,zynq-spi-r1p6 spi-zynq-qspi.c
AM335x ti,am3352-spi spi-omap2-mcspi.c
常见错误: compatible字符串中厂商名与型号之间必须使用英文逗号,且逗号后无空格。错误写法如 "fsl, imx6ul-ecspi" 会导致驱动匹配失败。

3.3 reg属性:寄存器资源映射

reg 属性描述SPI控制器的寄存器物理基地址和映射长度。内核在驱动探测阶段通过 platform_get_resource() 获取该信息,并调用 ioremap() 完成虚拟地址映射。

属性格式由父节点的 #address-cells 和 #size-cells 决定。对于ARM平台,通常为:

reg = <0x0208c000 0x4000>;

其中 0x0208c000 为寄存器基址,0x4000 为映射长度(16KB)。

平台 #address-cells #size-cells reg示例
ARM 32位 1 1 <0x0208c000 0x4000>
ARM 64位 2 2 <0x00 0x0208c000 0x00 0x4000>
RISC-V 2 2 <0x00 0x10040000 0x00 0x1000>

3.4 interrupts属性:中断资源描述

interrupts 属性向内核传递SPI控制器的中断号与触发类型。内核通过 platform_get_irq() 获取中断号,并在驱动初始化时调用 request_irq() 注册中断处理函数。

属性值格式依赖于中断控制器父节点定义的 #interrupt-cells:

interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;

其中:

  • GIC_SPI:共享外设中断类型(值为0),区别于PPI私有外设中断
  • 38:中断号,与数据手册中的SPI中断编号一致
  • IRQ_TYPE_LEVEL_HIGH:高电平触发,SPI控制器通常使用电平触发
中断触发类型宏 数值 适用场景
IRQ_TYPE_LEVEL_HIGH 4 SPI控制器标准中断(推荐)
IRQ_TYPE_LEVEL_LOW 8 低电平有效的中断控制器
IRQ_TYPE_EDGE_RISING 1 边沿触发,较少用于SPI
IRQ_TYPE_EDGE_FALLING 2 边沿触发,较少用于SPI
实践要点: SPI控制器中断通常配置为电平触发(LEVEL_HIGH),因为SPI传输过程中中断信号可能持续多个时钟周期,电平触发能可靠捕获。边沿触发在高速传输时容易丢失中断。

3.5 完整SPI控制器节点示例

以下是一个基于i.MX6UL平台的完整ECSPI控制器节点,整合了上述所有核心属性:

ecspi1: spi@0208c000 {
    compatible = "fsl,imx6ul-ecspi", "fsl,imx6q-ecspi";
    reg = <0x0208c000 0x4000>;
    interrupts = <GIC_SPI 38 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";
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";

    /* 从设备子节点在此处添加 */
};

3.6 属性与内核数据结构的映射关系

设备树解析完成后,上述属性被内核映射为 struct spi_controller 的关键字段:

设备树属性 → 内核数据结构映射 compatible reg interrupts clocks / dmas #address-cells / #size-cells of_match_table → spi_imx_dt_ids[] (驱动匹配表) platform_get_resource(pdev, IORESOURCE_MEM, 0) → base + ioremap() platform_get_irq(pdev, 0) → request_irq() (中断号 + 触发类型) clk_get() / dma_request_slave_channel() (时钟频率 + DMA通道) spi_controller->num_chipselect (从设备片选数量)
映射总结: 设备树中的每个属性都对应内核驱动中的特定API调用。理解这种映射关系,可以在调试SPI驱动问题时快速定位是设备树配置错误还是驱动代码逻辑问题。