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