SPI驱动调试与内核崩溃定位:设备驱动工程实践故障分析 - 第3节

3.1 设备树节点属性详解

SPI 设备树节点是驱动与硬件描述之间的桥梁。每个 SPI 从设备在设备树中对应一个子节点,挂载在 SPI 主控制器的节点之下。节点中必须包含 compatible、reg 和 spi-max-frequency 三个核心属性,同时可根据需要添加中断、DMA 和 pinctrl 描述。

3.1.1 核心属性:compatible / reg / spi-max-frequency

属性名 类型 必需 说明
compatible 字符串列表 是 设备驱动匹配标识,格式为 "厂商,型号"
reg u32 是 SPI 片选编号(0 基址)
spi-max-frequency u32 是 SPI 总线最大时钟频率,单位 Hz
关键规则:compatible 属性必须与驱动中 of_match_table 定义的字符串完全一致,否则驱动无法绑定设备。reg 属性值对应硬件片选线编号,多片选设备需注意与主控制器片选 GPIO 的映射关系。
/* 典型 SPI 设备节点示例 */
&spi1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = &spi1_pins;

    temperature_sensor: sensor@0 {
        compatible = "ti,lm70";
        reg = <0>;
        spi-max-frequency = <20000000>;
        spi-cpha;
        spi-cpol;
    };

    adc_device: adc@1 {
        compatible = "maxim,max1027";
        reg = <1>;
        spi-max-frequency = <10000000>;
        interrupts = <GIC_SPI 42 IRQ_TYPE_EDGE_RISING>;
    };
};
调试提示:spi-max-frequency 设置过高可能导致数据采样错误。调试时建议从较低频率(如 1MHz)开始,逐步提升至目标值,同时用示波器观察 MISO/MOSI 信号质量。

3.1.2 SPI 模式与时钟极性属性

SPI 总线支持四种工作模式,由时钟极性(CPOL)和时钟相位(CPHA)组合决定。设备树中通过以下布尔属性描述:

  • spi-cpol:时钟极性为高电平空闲(CPOL=1)
  • spi-cpha:数据在第二个时钟边沿采样(CPHA=1)
  • spi-cs-high:片选信号高电平有效(默认低电平有效)
  • spi-3wire:三线制 SPI,MISO 与 MOSI 复用
SPI 四种工作模式时序对比 模式 0 (CPOL=0, CPHA=0) 空闲时钟:低电平 采样边沿:上升沿 数据输出:下降沿 模式 1 (CPOL=0, CPHA=1) 空闲时钟:低电平 采样边沿:下降沿 数据输出:上升沿 模式 2 (CPOL=1, CPHA=0) 空闲时钟:高电平 采样边沿:下降沿 数据输出:上升沿 模式 3 (CPOL=1, CPHA=1) 空闲时钟:高电平 采样边沿:上升沿 数据输出:下降沿 设备树属性与模式对应关系 • 不声明 spi-cpol 和 spi-cpha → 默认模式 0 • 仅声明 spi-cpol → 模式 2 • 仅声明 spi-cpha → 模式 1 • 同时声明 spi-cpol 和 spi-cpha → 模式 3

3.2 中断与 DMA 描述

3.2.1 中断属性绑定

SPI 从设备的中断描述遵循标准设备树中断绑定规范。中断控制器类型、中断号和触发类型共同构成完整的中断描述。

/* 中断描述格式 */
interrupts = <中断控制器类型 中断号 触发类型>;

/* 实际示例:GPIO 中断 */
interrupt-parent = <&gpio1>;
interrupts = <15 IRQ_TYPE_EDGE_FALLING>;

/* 实际示例:GIC 中断 */
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
触发类型宏 数值 说明
IRQ_TYPE_EDGE_RISING 1 上升沿触发
IRQ_TYPE_EDGE_FALLING 2 下降沿触发
IRQ_TYPE_LEVEL_HIGH 4 高电平触发
IRQ_TYPE_LEVEL_LOW 8 低电平触发
常见错误:中断号与硬件连接不一致是 SPI 驱动调试中的高频问题。务必核对原理图中 SPI 从设备的 IRQ 引脚连接到了哪个 GPIO 或中断控制器,以及该引脚的中断号是否与设备树一致。使用 cat /proc/interrupts 可验证中断是否注册成功。

3.2.2 DMA 属性描述

SPI 控制器支持 DMA 传输时,需要在设备树中描述 DMA 通道和请求线。DMA 属性通常定义在主控制器节点中,从设备节点通过继承获得 DMA 能力。

/* SPI 主控制器 DMA 描述 */
spi2: spi@ff110000 {
    compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi";
    reg = <0x0 0xff110000 0x0 0x1000>;
    interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
    dmas = <&dmac 1 7>, <&dmac 0 7>;
    dma-names = "tx", "rx";
    #address-cells = <1>;
    #size-cells = <0>;

    /* 从设备节点继承 DMA 能力 */
    spi_device@0 {
        compatible = "vendor,device";
        reg = <0>;
        spi-max-frequency = <50000000>;
    };
};
SPI DMA 传输数据流 系统内存 DMA 控制器 通道 0: RX 通道 1: TX SPI 控制器 DMA 请求 数据搬运 SPI 总线 外设 DMA 传输优势 • 无需 CPU 逐字节搬运数据,大幅降低 CPU 占用率 • 适用于高速 SPI 设备(如 ADC、显示控制器、无线模块)

3.3 pinctrl 配置

3.3.1 pinctrl 绑定规则

pinctrl 子系统负责 SPI 引脚的复用功能配置。设备树中通过 pinctrl-names 和 pinctrl-0 属性将引脚状态与设备状态关联。

/* pinctrl 配置结构 */
&spi1 {
    pinctrl-names = "default", "sleep";
    pinctrl-0 = &spi1_pins_active;
    pinctrl-1 = &spi1_pins_sleep;
    status = "okay";
};

/* 引脚节点定义 */
&pinctrl {
    spi1 {
        spi1_pins_active: spi1-active {
            rockchip,pins = <1 RK_PB0 RK_FUNC_2 &pcfg_pull_up_drv_level_0>,
                            <1 RK_PB1 RK_FUNC_2 &pcfg_pull_up_drv_level_0>,
                            <1 RK_PB2 RK_FUNC_2 &pcfg_pull_up_drv_level_0>,
                            <1 RK_PB3 RK_FUNC_2 &pcfg_pull_up_drv_level_0>;
        };

        spi1_pins_sleep: spi1-sleep {
            rockchip,pins = <1 RK_PB0 RK_FUNC_GPIO &pcfg_output_low>,
                            <1 RK_PB1 RK_FUNC_GPIO &pcfg_output_low>,
                            <1 RK_PB2 RK_FUNC_GPIO &pcfg_output_low>,
                            <1 RK_PB3 RK_FUNC_GPIO &pcfg_output_low>;
        };
    };
};

3.3.2 引脚配置参数解析

参数位置 含义 示例值
bank GPIO 组编号 1 (GPIO1)
pin 组内引脚编号 RK_PB0 (GPIO1_B0)
func 复用功能编号 RK_FUNC_2 (SPI 功能)
pull/drive 上下拉与驱动能力 pcfg_pull_up_drv_level_0
调试技巧:当 SPI 通信不稳定时,检查 pinctrl 配置是否正确。使用 cat /sys/kernel/debug/gpio 查看引脚复用状态,确认引脚是否被正确配置为 SPI 功能而非 GPIO。常见问题包括:引脚复用冲突(被其他外设占用)、上下拉配置错误导致信号电平异常。

3.4 完整设备树节点综合示例

以下是一个完整的 SPI 设备树节点,集成了所有核心属性、中断、DMA 和 pinctrl 配置:

/* 完整 SPI 设备树节点 */
&spi0 {
    status = "okay";
    pinctrl-names = "default", "sleep";
    pinctrl-0 = &spi0_pins_active;
    pinctrl-1 = &spi0_pins_sleep;
    dmas = <&dmac 0 2>, <&dmac 1 2>;
    dma-names = "tx", "rx";

    /* SPI NOR Flash */
    flash@0 {
        compatible = "jedec,spi-nor";
        reg = <0>;
        spi-max-frequency = <104000000>;
        spi-cpol;
        spi-cpha;
        m25p,fast-read;
        broken-flash-reset;

        partitions {
            compatible = "fixed-partitions";
            #address-cells = <1>;
            #size-cells = <1>;

            partition@0 {
                label = "bootloader";
                reg = <0x0 0x100000>;
                read-only;
            };
            partition@100000 {
                label = "kernel";
                reg = <0x100000 0x500000>;
            };
        };
    };

    /* SPI 温度传感器 */
    sensor@1 {
        compatible = "maxim,max31865";
        reg = <1>;
        spi-max-frequency = <5000000>;
        interrupts = <&gpio2 10 IRQ_TYPE_EDGE_FALLING>;
        interrupt-names = "ready";
    };
};
SPI 设备树节点层次结构 SPI 主控制器节点 核心属性 compatible reg spi-max-frequency 中断描述 interrupts interrupt-parent interrupt-names DMA 描述 dmas dma-names pinctrl 配置 pinctrl-names pinctrl-0 pinctrl-1 SPI 从设备子节点 (reg = <0>) 从设备节点继承主控制器的 DMA 和 pinctrl 配置,只需声明自身专属属性

3.5 常见绑定错误与调试方法

错误类型 现象 排查方法
compatible 不匹配 驱动 probe 函数未调用 检查 /sys/bus/spi/devices/ 下设备名称
reg 片选冲突 多个设备使用同一片选 核对原理图片选连接
spi-max-frequency 过高 数据错位、CRC 错误 降低频率测试,观察信号质量
中断号错误 中断不触发或触发异常 使用 cat /proc/interrupts 验证
pinctrl 配置错误 引脚无输出或电平异常 检查 /sys/kernel/debug/pinctrl/
调试黄金法则:当 SPI 设备无法正常工作时,首先检查设备树是否被正确解析。使用 dtc -I dtb -O dts /sys/firmware/fdt 反编译当前使用的设备树,确认节点属性与预期一致。这是定位设备树绑定问题的第一步,也是最有效的一步。