4. 设备树中ADC节点通用属性
ADC设备节点在设备树中通过一组标准属性描述其硬件连接、资源需求和运行时配置。这些属性是操作系统(尤其是Linux内核)识别、初始化并驱动ADC硬件的关键依据。本节将逐一解析compatible、reg、interrupts、clocks和vref-supply这五个核心通用属性。
4.1 属性总览与功能矩阵
下表总结了ADC节点中五个通用属性的功能、数据类型和典型用途,便于快速对照和查阅。
| 属性名 | 数据类型 | 功能描述 | 典型值/示例 |
|---|---|---|---|
compatible |
字符串列表 | 指定设备与驱动程序的匹配标识 | "ti,am3359-adc", "ti,am3358-adc" |
reg |
地址/长度对列表 | 定义设备在总线上的地址空间(寄存器基址和大小) | <0x44e0d000 0x1000> |
interrupts |
中断描述列表 | 描述设备使用的中断线、触发类型和优先级 | |
clocks |
phandle + 时钟标识符列表 | 指定设备使用的时钟源(如ADC采样时钟、总线时钟) | <&adc_clk 0> |
vref-supply |
phandle | 引用电压调节器节点,提供ADC参考电压 | <&vref_3v3> |
vref-supply 是一个 supply 属性(通常以 -supply 结尾),它通过 phandle 引用一个 regulator 节点,而不是直接提供数值。这种设计将电压管理解耦到电源管理子系统中。
4.2 属性详解与实战配置
4.2.1 compatible —— 驱动匹配的“身份证”
compatible 属性是设备树中最重要的属性之一。它包含一个或多个字符串,用于将设备节点与内核中的驱动程序进行匹配。匹配顺序遵循“最具体优先”原则:内核会从列表的第一个字符串开始查找匹配的驱动,如果找不到,则尝试下一个。
adc: adc@44e0d000 {
compatible = "ti,am3359-adc", "ti,am3358-adc", "ti,am33xx-adc";
reg = <0x44e0d000 0x1000>;
...
};
在这个例子中,内核会依次尝试匹配 ti,am3359-adc、ti,am3358-adc 和 ti,am33xx-adc。这种设计允许一个驱动程序支持多个硬件变体,同时保留了为特定型号优化驱动的可能性。
compatible 写成过于通用的名称(如 "adc-generic"),这会导致驱动匹配混乱。始终遵循 "厂商,型号" 的命名规范,并优先使用最具体的型号字符串。
4.2.2 reg —— 硬件地址空间的“地图”
reg 属性定义了设备在系统总线上的地址区域。每个 reg 条目由一个地址和一个长度组成,分别表示寄存器基地址和映射区域的大小。对于ADC设备,通常只有一个地址区域,即其控制寄存器和数据寄存器所在的地址块。
adc@483c0000 {
compatible = "ti,am3359-adc";
reg = <0x483c0000 0x2000>; // 基地址 0x483c0000,大小 8KB
interrupts = <16>;
...
};
在Linux内核中,驱动程序通过 platform_get_resource() 或 of_iomap() 函数获取 reg 属性定义的地址范围,并将其映射到内核虚拟地址空间,从而实现对硬件寄存器的读写。
4.2.3 interrupts —— 异步事件的“通知线”
interrupts 属性描述了设备如何向处理器发送中断请求。对于ADC设备,中断通常用于指示转换完成、FIFO满或阈值触发等事件。中断描述符的格式取决于中断控制器(如GIC、AINTC等)。
adc@483c0000 {
compatible = "ti,am3359-adc";
reg = <0x483c0000 0x2000>;
interrupts = ;
interrupt-parent = <&gic>;
...
};
上述示例中:
GIC_SPI表示共享外设中断类型。16是中断号。IRQ_TYPE_LEVEL_HIGH指定中断触发方式为高电平触发。interrupt-parent属性显式指定中断控制器为&gic。
interrupt-parent 属性明确指定父控制器,避免中断路由混乱。对于只有一个中断控制器的简单系统,此属性可以省略。
4.2.4 clocks —— 设备运行的“心跳”
clocks 属性列出设备所需的所有时钟源。ADC设备通常需要至少一个时钟:采样时钟(控制转换速率)和/或总线接口时钟(控制寄存器访问)。时钟通过 phandle 和时钟标识符(通常是索引)来引用。
adc@483c0000 {
compatible = "ti,am3359-adc";
reg = <0x483c0000 0x2000>;
interrupts = <16>;
clocks = <&adc_tsc_fck>, <&adc_tsc_ick>;
clock-names = "fck", "ick";
...
};
这里:
clocks属性包含两个时钟引用:adc_tsc_fck(功能时钟)和adc_tsc_ick(接口时钟)。clock-names属性为每个时钟提供一个名称,驱动程序可以通过名称(如"fck")而不是索引来获取时钟,提高代码可读性和健壮性。
4.2.5 vref-supply —— 转换精度的“基准尺”
vref-supply 属性引用一个电压调节器(regulator)节点,该节点提供ADC的参考电压。参考电压直接影响ADC的转换范围和精度:对于N位ADC,输入电压 V_in 与转换值 D 的关系为 V_in = D * (V_ref / 2^N)。
vref_3v3: regulator-vref {
compatible = "regulator-fixed";
regulator-name = "vref-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
adc@483c0000 {
compatible = "ti,am3359-adc";
reg = <0x483c0000 0x2000>;
interrupts = <16>;
clocks = <&adc_tsc_fck>;
vref-supply = <&vref_3v3>; // 引用 3.3V 参考电压
...
};
-supply 后缀? 设备树规范中,以 -supply 结尾的属性专门用于引用电源/电压调节器。这种命名约定使得内核的电源管理框架(regulator framework)能够自动识别并管理这些电源依赖关系,例如在系统休眠时关闭ADC的参考电压以节省功耗。
4.3 属性关系与数据流:SVG架构图
下图展示了ADC节点中五个通用属性之间的逻辑关系以及它们如何协同工作,为驱动程序提供完整的硬件上下文。
4.4 完整ADC节点示例
综合以上五个属性,下面给出一个完整的ADC设备树节点示例,展示它们如何协同描述一个实际的硬件设备。
/ {
// 参考电压调节器
vref_2v5: regulator-vref-2v5 {
compatible = "regulator-fixed";
regulator-name = "vref-2v5";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
regulator-always-on;
};
// 时钟源
adc_clk: clk-adc {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>; // 24 MHz
};
// 中断控制器(假设使用GIC)
gic: interrupt-controller@48200000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x48200000 0x1000>,
<0x48200100 0x100>;
};
// ADC设备节点
adc@483c0000 {
compatible = "ti,am3359-adc", "ti,am33xx-adc";
reg = <0x483c0000 0x2000>;
interrupts = ;
interrupt-parent = <&gic>;
clocks = <&adc_clk>;
clock-names = "adc_clk";
vref-supply = <&vref_2v5>;
// 通道配置(下一节详细讲解)
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
label = "AIN0";
};
channel@1 {
reg = <1>;
label = "AIN1";
};
};
};
#address-cells 和 #size-cells 用于定义子节点(通道)的地址格式。这是设备树中实现“节点嵌套”和“子节点寻址”的关键机制,将在下一节“通道绑定”中详细展开。
4.5 常见错误与排查清单
在编写ADC设备树节点时,以下错误最为常见。使用此清单进行自查,可以有效减少调试时间。
| 错误类型 | 典型表现 | 排查方法 |
|---|---|---|
compatible 拼写错误 |
驱动无法加载,内核报 No driver found |
检查 /sys/bus/platform/devices/.../uevent 中的 OF_COMPATIBLE 值 |
reg 地址冲突 |
系统启动时挂死或 ioremap 失败 |
使用 cat /proc/iomem 检查地址范围是否重叠 |
interrupts 号错误 |
中断无法触发,或触发错误的中断处理函数 | 查看 /proc/interrupts 确认中断号是否注册 |
clocks 引用无效 |
时钟无法使能,ADC采样值异常或为零 | 检查 /sys/kernel/debug/clk/clk_summary 确认时钟状态 |
vref-supply 未定义 |
驱动 probe 失败,返回 -EPROBE_DEFER |
确认 regulator 节点存在且状态为 okay |
dtc -I dtb -O dts 反编译设备树二进制文件(DTB),检查实际加载的设备树内容是否与预期一致。这是验证设备树语法和属性值的最终手段。