3. 设备树与ACPI在Snapdragon上的适配
在Snapdragon平台上做硬件适配,绕不开两套硬件描述机制:设备树(Device Tree,DT)和ACPI。前者源自ARM嵌入式生态,后者来自PC/服务器体系。高通平台在移动端、Windows on Snapdragon以及部分服务器/边缘计算产品上同时支持两者,因此理解它们的组织方式、适用边界以及节点配置细节,是系统级调试的基本功。
Device Tree源文件结构与DTS/DTSI组织方式
设备树源文件以.dts和.dtsi为后缀。DTS是最终编译单元,DTSI是被包含的公共片段。编译流程为:dtc将DTS编译为DTB(Device Tree Blob),Bootloader在启动时把DTB传给内核,内核展开为/proc/device-tree或/sys/firmware/devicetree/base下的节点树。
典型的高通平台设备树组织采用“分层包含”策略:SoC级DTSI描述芯片内部控制器(如soc.dtsi、qupv3.dtsi),板级DTSI描述具体外设连接(如pmic.dtsi、display.dtsi),最终DTS只做板级差异覆盖。例如:
// 文件组织示意
arch/arm64/boot/dts/vendor/
├── soc.dtsi // SoC内部控制器
├── qupv3_se0_i2c.dtsi // QUPv3 SE0配置为I2C
├── qupv3_se1_spi.dtsi // QUPv3 SE1配置为SPI
├── pmic.dtsi // PMIC与GPIO
└── board-idp.dts // 最终板级DTS
DTSI中大量使用&label引用和status属性覆盖。例如在SoC级DTSI中,QUPv3 SE0默认status = "disabled",板级DTSI中通过&qupv3_se0_i2c { status = "okay"; }启用。这种“默认关闭、按需启用”的机制避免了未使用控制器占用引脚和时钟资源。
ACPI与DT的取舍
ACPI在Snapdragon上的引入主要是为了兼容Windows on Snapdragon和服务器生态。ACPI使用ASL(ACPI Source Language)编写,编译为AML字节码,由OSPM解析。与DT相比,ACPI的优势在于标准化程度高、操作系统无关、支持运行时热插拔和电源管理事件;劣势是描述能力相对固定,对非标准外设的扩展不如DT灵活。
取舍原则可以归纳为:
- 移动Android平台:优先使用DT,因为Android内核和驱动生态对DT支持最成熟,DTSI分层组织便于多项目复用。
- Windows on Snapdragon:必须使用ACPI,因为Windows不解析DT,所有外设、GPIO、I2C、SPI、UART都通过ACPI命名空间描述。
- 服务器/边缘计算:根据OS选择,Linux可同时支持DT和ACPI,但同一设备不应混用两套描述,避免资源冲突。
实际项目中,同一颗SoC可能同时维护DT和ACPI两套描述文件,但引脚、时钟、电源域等硬件资源必须保持一致,否则会出现“DT下正常、ACPI下外设不工作”的典型问题。
GPIO/I2C/SPI/UART节点配置与调试
以QUPv3(Qualcomm Universal Peripheral)为例,SE(Serial Engine)可以复用为I2C、SPI或UART。配置时首先在DTSI中确定协议类型,再配置引脚、时钟和电源。以下是一个I2C节点的示例:
&qupv3_se0_i2c {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se0_i2c_active>;
pinctrl-1 = <&qupv3_se0_i2c_sleep>;
touch@5d {
compatible = "vendor,touch";
reg = <0x5d>;
interrupt-parent = <&tlmm>;
interrupts = <65 0x2002>;
reset-gpios = <&tlmm 66 GPIO_ACTIVE_LOW>;
vdd-supply = <&pm8350_l6>;
};
};
关键点:clock-frequency决定I2C速率,pinctrl-0/1分别对应工作和睡眠状态的引脚配置,interrupts中的0x2002表示下降沿触发,reset-gpios使用TLMM(Top Level Mode Multiplexer)的GPIO编号。SPI节点额外需要spi-max-frequency和cs-gpios;UART节点需要cts-rts流控配置。
调试时,DT展开后可通过以下路径确认节点是否生效:
# 查看I2C控制器是否注册
ls /sys/bus/i2c/devices/
# 查看GPIO占用
cat /sys/kernel/debug/gpio
# 查看pinctrl状态
cat /sys/kernel/debug/pinctrl/*/pinmux-pins
若I2C设备未出现,优先检查status是否为okay、reg地址是否与硬件一致、pinctrl是否被其他节点占用。若GPIO申请失败,查看/sys/kernel/debug/gpio中该引脚是否已被其他驱动占用。ACPI下则通过/sys/firmware/acpi/tables/和acpidump检查AML中设备是否被正确声明。
实际适配中的常见问题与判断依据
在Snapdragon平台上,DT与ACPI混用或描述不一致是高频问题。例如某项目在Android下I2C触摸屏工作正常,切换到Windows后触摸无响应。排查时先确认ACPI命名空间中是否存在对应设备节点,再检查_CRS中的I2C地址和GPIO中断号是否与硬件原理图一致。若ACPI中缺少_DSD属性,驱动可能无法获取复位GPIO,导致设备无法初始化。
另一个典型问题是引脚复用冲突。QUPv3的SE引脚在DT中通过pinctrl配置为I2C功能,但若同一引脚在ACPI中被声明为GPIO,则会出现“DT下正常、ACPI下总线无波形”的现象。判断依据是查看/sys/kernel/debug/pinctrl/下该引脚的function是否为qup0_se0,若不是则说明被其他节点占用。
调试UART时,若dmesg中出现ttyMSM0: failed to request GPIO,说明cts-rts流控引脚被其他驱动占用。此时应检查DTSI中该UART节点的pinctrl-0是否包含流控引脚,以及是否有其他节点引用了相同GPIO。ACPI下则检查_CRS中UART的UartSerialBus资源描述是否完整。
/proc/device-tree和/sys/kernel/debug,ACPI侧依赖acpidump、acpiexec和/sys/firmware/acpi/tables。建议在项目初期就建立两套描述文件的交叉检查清单,确保GPIO编号、I2C地址、时钟频率、电源域等关键参数一致。
总结来说,Snapdragon上的DT与ACPI适配不是二选一的问题,而是根据目标操作系统和产品形态选择合适路径,并在同一硬件平台上保持两套描述的资源一致性。掌握DTS/DTSI的分层组织、ACPI的ASL编写规范,以及GPIO/I2C/SPI/UART节点的配置与调试方法,才能在实际项目中快速定位和解决外设不工作、引脚冲突、驱动匹配失败等问题。