4. 设备树中ADC节点通用属性

ADC设备节点在设备树中通过一组标准属性描述其硬件连接、资源需求和运行时配置。这些属性是操作系统(尤其是Linux内核)识别、初始化并驱动ADC硬件的关键依据。本节将逐一解析compatible、reg、interrupts、clocks和vref-supply这五个核心通用属性。

核心要点: 设备树中的ADC节点本质上是将硬件拓扑、地址映射、中断路由、时钟依赖和电源参考等物理特性,转化为一种结构化的、与平台无关的描述语言。理解这些属性是编写正确ADC设备树节点的前提。

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节点中五个通用属性之间的逻辑关系以及它们如何协同工作,为驱动程序提供完整的硬件上下文。

ADC设备树节点通用属性关系图 ADC设备树节点通用属性关系与数据流 ADC 设备节点 compatible reg interrupts clocks vref-supply 属性作用说明: • compatible → 驱动匹配:内核根据此字符串查找对应的ADC驱动程序。 • reg → 地址映射:驱动程序通过此属性获取寄存器基地址并映射到虚拟空间。 • interrupts → 中断处理:转换完成或错误时,硬件通过此中断线通知CPU。 • clocks → 时钟控制:驱动使能/禁能时钟以控制ADC采样速率和功耗。 • vref-supply → 参考电压:驱动通过regulator框架获取参考电压值,用于计算实际电压。

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),检查实际加载的设备树内容是否与预期一致。这是验证设备树语法和属性值的最终手段。