4. SPI设备树绑定:设备树中SPI节点的标准属性与解析流程

在嵌入式Linux系统中,设备树(Device Tree)是描述硬件拓扑结构的核心机制。对于SPI子系统而言,设备树节点不仅定义了SPI控制器的硬件资源(寄存器地址、中断号),还枚举了挂载在总线上的所有从设备及其配置参数。本章将深入解析SPI设备树绑定的标准属性、多从机拓扑示例以及内核解析流程。

4.1 SPI控制器节点与从设备节点的标准属性

一个完整的SPI设备树绑定包含两个层级:SPI控制器节点(即SPI主机适配器)和SPI从设备节点(即挂载在总线上的外设)。控制器节点通常由SoC厂商在.dtsi文件中定义,而从设备节点由板级工程师在.dts文件中添加。

4.1.1 SPI控制器节点核心属性

属性名 类型 描述 是否必需
compatible 字符串列表 匹配SPI控制器驱动,如"snps,dw-apb-ssi"、"fsl,imx6q-ecspi" 是
reg 地址/长度对 控制器寄存器基地址和范围 是
interrupts 中断描述 SPI控制器中断号及触发类型 是
clocks phandle列表 SPI模块时钟和总线时钟 是
#address-cells u32 子节点地址字段宽度,通常为1 是
#size-cells u32 子节点大小字段宽度,通常为0 是
cs-gpios GPIO列表 指定片选信号使用的GPIO,用于GPIO模拟片选 可选
dmas phandle列表 DMA引擎和通道描述 可选

4.1.2 SPI从设备节点标准属性

属性名 类型 描述 是否必需
compatible 字符串列表 匹配从设备驱动,如"microchip,mcp2515"、"ti,ads1256" 是
reg u32 从设备片选编号(0-based),对应控制器硬件片选线 是
spi-max-frequency u32 该从设备支持的最大SPI时钟频率,单位Hz 是
spi-cpol 空属性 时钟极性为高电平空闲(CPOL=1) 可选
spi-cpha 空属性 时钟相位为第二个边沿采样(CPHA=1) 可选
spi-cs-high 空属性 片选信号高电平有效(默认低电平有效) 可选
spi-3wire 空属性 三线制SPI(无MISO/MOSI区分) 可选
spi-lsb-first 空属性 LSB优先传输(默认MSB优先) 可选
interrupts 中断描述 从设备的中断输出引脚 可选
关键理解: reg属性在SPI从设备节点中代表片选编号,而非地址。内核通过reg值将设备绑定到控制器的特定片选线。如果使用cs-gpios,则reg值对应cs-gpios列表中的索引。

4.2 多从机设备树示例

以下是一个完整的设备树片段,展示了一个SPI控制器挂载三个不同从设备的典型场景:一个CAN控制器(MCP2515)、一个ADC(ADS1256)和一个SPI Flash(W25Q128)。

/* SPI控制器节点定义 */
&ecspi1 {
    pinctrl-names = "default";
    pinctrl-0 = &pinctrl_ecspi1;
    status = "okay";
    cs-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>,
               <&gpio5 10 GPIO_ACTIVE_LOW>,
               <&gpio5 11 GPIO_ACTIVE_LOW>;

    /* 从设备0:MCP2515 CAN控制器 */
    can0: can@0 {
        compatible = "microchip,mcp2515";
        reg = <0>;                    /* 使用cs-gpios[0] */
        spi-max-frequency = <10000000>;
        clocks = <&clks IMX6QDL_CLK_CAN>;
        interrupt-parent = <&gpio1>;
        interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
        spi-cpol;
        spi-cpha;
        status = "okay";
    };

    /* 从设备1:ADS1256 ADC */
    adc1: adc@1 {
        compatible = "ti,ads1256";
        reg = <1>;                    /* 使用cs-gpios[1] */
        spi-max-frequency = <19200000>;
        interrupt-parent = <&gpio1>;
        interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
        vref-supply = <®_3v3>;
        status = "okay";
    };

    /* 从设备2:W25Q128 SPI Flash */
    flash2: flash@2 {
        compatible = "winbond,w25q128", "jedec,spi-nor";
        reg = <2>;                    /* 使用cs-gpios[2] */
        spi-max-frequency = <80000000>;
        m25p,fast-read;
        status = "okay";
    };
};
常见陷阱: 当使用cs-gpios属性时,控制器原有的硬件片选线被禁用,所有片选信号均由GPIO模拟。此时reg值必须从0开始连续编号,且数量与cs-gpios列表长度一致。如果硬件片选和GPIO片选混用,需要特殊处理,通常不推荐。

4.3 设备树解析流程

内核SPI子系统在启动过程中,按照以下流程解析设备树并创建SPI设备:

  1. SPI控制器驱动注册:驱动通过module_platform_driver()注册,probe()函数被调用。
  2. 读取控制器资源:使用platform_get_resource()获取寄存器地址、中断号等。
  3. 解析cs-gpios:调用of_get_named_gpio_flags()解析GPIO片选列表,存储在spi_master的cs_gpios数组中。
  4. 注册SPI控制器:调用spi_register_master()将控制器注册到SPI核心。
  5. 扫描子节点:SPI核心遍历控制器设备树节点的所有子节点,对每个子节点调用of_register_spi_devices()。
  6. 创建spi_device:解析子节点的reg、spi-max-frequency、模式标志等属性,填充spi_device结构体。
  7. 绑定从设备驱动:根据compatible匹配驱动,调用驱动的probe()函数。
解析核心函数: of_spi_parse_dt()是解析单个从设备节点的核心函数,它读取spi-max-frequency、spi-cpol、spi-cpha等属性,并将模式标志写入spi_device->mode。该函数位于drivers/spi/spi.c中。
SPI设备树解析流程 设备树二进制(DTB) SPI控制器驱动 probe() 调用 解析 reg/interrupts/cs-gpios spi_register_master() of_register_spi_devices() 创建 spi_device 并匹配驱动 关键解析函数 of_spi_parse_dt() of_get_named_gpio_flags() spi_alloc_device() spi_add_device()

4.4 设备树属性与spi_device结构体的映射

设备树解析完成后,属性值被填充到内核的struct spi_device中。下表展示了关键映射关系:

设备树属性 spi_device成员 说明
reg chip_select 片选编号,用于选择cs_gpios数组中的GPIO
spi-max-frequency max_speed_hz 最大时钟频率,驱动通信时不得超过此值
spi-cpol mode中的SPI_CPOL位 时钟极性,决定空闲时时钟电平
spi-cpha mode中的SPI_CPHA位 时钟相位,决定数据采样边沿
spi-cs-high mode中的SPI_CS_HIGH位 片选有效电平,默认低电平有效
spi-3wire mode中的SPI_3WIRE位 三线模式,MISO和MOSI复用
spi-lsb-first mode中的SPI_LSB_FIRST位 LSB优先传输模式

4.5 实战:验证设备树解析结果

在系统启动后,可以通过以下方法验证设备树解析是否正确:

  • 查看/sys/bus/spi/devices/:该目录下会列出所有已注册的SPI设备,格式为spiX.Y,其中X是控制器编号,Y是片选编号。
  • 使用dtc反编译:dtc -I fs -O dts /proc/device-tree可以查看当前系统使用的设备树。
  • 检查内核日志:dmesg | grep spi可以查看SPI子系统的解析和注册信息。
# 查看SPI设备列表
ls -l /sys/bus/spi/devices/
# 输出示例:
# spi0.0 -> ../../../devices/platform/ecspi1/spi_master/spi0/spi0.0
# spi0.1 -> ../../../devices/platform/ecspi1/spi_master/spi0/spi0.1
# spi0.2 -> ../../../devices/platform/ecspi1/spi_master/spi0/spi0.2

# 查看某个设备的属性
cat /sys/bus/spi/devices/spi0.0/of_node/spi-max-frequency
# 输出:10000000
调试技巧: 如果SPI设备没有出现在/sys/bus/spi/devices/中,首先检查设备树节点是否设置了status = "okay";其次确认compatible属性是否与驱动匹配;最后检查cs-gpios对应的GPIO是否被其他驱动占用。

4.6 本章小结