2. 时钟节点核心属性:详解 compatible、clocks、clock-names、clock-frequency、assigned-clocks 等关键属性

设备树中时钟节点的核心属性是驱动与硬件时钟控制器之间的契约。正确配置这些属性,是时钟驱动正常工作的前提。本节将逐一拆解 compatible、clocks、clock-names、clock-frequency、assigned-clocks 等关键属性的语义、配置规范及常见错误。

2.1 属性总览与功能分类

时钟节点属性可分为三类:标识属性(匹配驱动)、连接属性(描述时钟树拓扑)、配置属性(设定初始频率与父时钟)。下表列出各属性的核心作用:

属性名 分类 作用 是否必需
compatible 标识 匹配时钟控制器驱动 是
clocks 连接 声明输入时钟源(phandle + 索引) 视情况
clock-names 连接 为 clocks 条目提供语义名称 推荐
clock-frequency 配置 指定固定频率(通常用于固定时钟) 视情况
assigned-clocks 配置 Bootloader 阶段重设时钟频率或父时钟 可选
assigned-clock-parents 配置 与 assigned-clocks 配合,指定父时钟 可选
assigned-clock-rates 配置 与 assigned-clocks 配合,指定目标频率 可选
经验法则: 对于 SoC 内部的时钟控制器,compatible 和 clocks(如果有输入源)是必须的;对于板级固定频率振荡器,compatible 和 clock-frequency 即可。

2.2 compatible —— 驱动匹配的钥匙

compatible 是设备树中最关键的标识属性。驱动通过 of_match_table 与设备树节点的 compatible 字符串进行匹配。格式为 "厂商,型号",例如:

clk_uart: clock-controller@ff0a0000 {
    compatible = "samsung,exynos-uart-clock", "fixed-clock";
    // ...
};

匹配优先级:驱动会按 of_match_table 中的顺序依次尝试,设备树中 compatible 的第一个字符串优先级最高。常见错误包括:

  • 字符串拼写错误:大小写、连字符、下划线不匹配。
  • 缺少 fallback:未包含兼容的通用 compatible,导致旧内核无法匹配。
  • 驱动未编译:即使 compatible 正确,驱动未选入内核也会导致匹配失败。

2.3 clocks 与 clock-names —— 构建时钟树拓扑

clocks 属性是一个 phandle + 时钟索引 的列表,描述当前时钟控制器的输入源。每个条目指向一个上游时钟节点。例如:

clk_pll: pll@1000 {
    compatible = "vendor,my-pll";
    clocks = <&osc 0>, <&clk_ref 1>;
    clock-names = "osc", "ref";
};

上述代码表示:

  • 第一个输入源是 osc 节点的第 0 路输出。
  • 第二个输入源是 clk_ref 节点的第 1 路输出。
  • clock-names 为每个输入源赋予语义名称,驱动可通过 of_clk_get_by_name() 按名称获取。
常见陷阱: clocks 的顺序必须与 clock-names 一一对应。如果只写 clocks 而不写 clock-names,驱动只能通过索引访问,代码可读性差且容易出错。

2.4 clock-frequency —— 固定频率时钟的声明

对于板级晶振或固定频率振荡器,clock-frequency 直接指定输出频率,单位是 Hz。典型用法:

osc24m: oscillator {
    compatible = "fixed-clock";
    #clock-cells = <0>;
    clock-frequency = <24000000>;
};

驱动读取该属性后,直接注册一个固定频率时钟。常见问题:

  • 频率值写错:例如 24MHz 写成 24000(缺少三个零)。
  • 缺少 #clock-cells:固定时钟通常设为 0,表示输出单路时钟。
  • 与 assigned-clocks 冲突:如果同时使用 assigned-clocks 修改该节点频率,可能导致预期外的覆盖。

2.5 assigned-clocks 系列 —— Bootloader 阶段的时钟重配置

assigned-clocks、assigned-clock-parents、assigned-clock-rates 三个属性配合使用,在 Bootloader(如 U-Boot)或内核早期初始化阶段,对时钟进行重配置。典型场景:

&uart0 {
    assigned-clocks = <&clk_uart 0>;
    assigned-clock-parents = <&clk_pll 1>;
    assigned-clock-rates = <115200>;
};

上述配置表示:将 clk_uart 的第 0 路输出时钟的父时钟设置为 clk_pll 的第 1 路,并将频率设置为 115200 Hz。

使用注意事项:

  • 属性位置:通常放在使用该时钟的 消费者节点 中,而非时钟控制器节点。
  • 执行时机:在时钟框架初始化完成后、驱动 probe 之前执行。
  • 依赖关系:如果父时钟未准备好,配置会延迟或失败。
调试技巧: 如果发现某个外设时钟频率不对,首先检查该外设节点是否有 assigned-clocks 属性,以及 assigned-clock-parents 是否指向了正确的 PLL 输出。

2.6 核心属性关系图

下图展示了时钟节点核心属性之间的逻辑关系与数据流向:

时钟节点核心属性关系图 时钟节点核心属性关系图 标识属性 compatible "厂商,型号" 驱动匹配入口 连接属性 clocks clock-names 构建时钟树拓扑 配置属性 clock-frequency assigned-clocks assigned-clock-parents/rates 时钟框架注册 & 驱动 probe 最终生成 clk_hw 并加入时钟树 常见错误排查点 ① compatible 拼写错误 → 驱动不匹配 ② clocks 顺序与 clock-names 不对应 → 获取时钟失败

2.7 典型配置示例与对比

以下对比展示一个 固定频率晶振 与一个 可配置 PLL 的节点配置差异:

对比项 固定晶振 (fixed-clock) 可配置 PLL
compatible "fixed-clock" "vendor,my-pll"
#clock-cells <0> <1>(多路输出)
clocks 无(自身是根时钟) <&osc 0>
clock-frequency <24000000> 无(由 assigned-clocks 配置)
assigned-clocks 通常不写 在消费者节点中配置

2.8 调试检查清单

当遇到时钟相关驱动 probe 失败或频率异常时,按以下清单逐一排查:

  1. 检查 compatible 是否匹配:cat /sys/firmware/devicetree/base/.../compatible 与驱动中的 of_match_table 对比。
  2. 检查 clocks 是否存在且顺序正确:使用 clk_summary 或 debugfs 查看时钟树连接。
  3. 检查 clock-names 是否与驱动代码一致:驱动调用 devm_clk_get(dev, "name") 时名称必须完全匹配。
  4. 检查 clock-frequency 单位:确认数值是 Hz,而非 kHz 或 MHz。
  5. 检查 assigned-clocks 的 phandle 是否有效:确保引用的时钟节点存在且已注册。
  6. 检查父时钟是否已使能:如果父时钟未使能,子时钟可能无法获取有效频率。
快速验证: 在驱动 probe 函数中添加 dev_info(dev, "clock rate: %lu\n", clk_get_rate(clk));,对比实际值与预期值是否一致。