4、时钟提供者(Clock Provider)绑定:固定频率时钟(fixed-clock)、晶振时钟绑定示例

时钟提供者(Clock Provider)是设备树中负责产生时钟信号的节点。在嵌入式系统中,最基础的时钟源通常来自板级晶振或固定频率振荡器。设备树通过标准化的绑定接口描述这些时钟源,使操作系统内核(尤其是 Linux clk 框架)能够正确识别、注册和使用它们。

本章聚焦于两种最常用的时钟提供者绑定:固定频率时钟(fixed-clock)和晶振时钟(crystal-clock)。前者适用于任何恒定频率的时钟源,后者专用于描述晶体振荡器及其负载电容等物理特性。

4.1 fixed-clock 绑定规范

fixed-clock 是设备树中最简单的时钟提供者绑定,用于描述频率固定、无需配置的时钟源。其核心属性如下表所示:

属性名 类型 是否必需 描述
compatible string 必需 必须为 "fixed-clock"
#clock-cells u32 必需 固定为 0,表示该时钟提供者不输出多个时钟
clock-frequency u32 必需 时钟频率,单位 Hz
clock-output-names string 可选 时钟输出名称,用于调试和符号链接

关键约束:#clock-cells = <0> 意味着该节点只产生一个时钟输出,消费者在 clocks 属性中引用时不需要额外指定时钟索引。

以下是一个典型的 fixed-clock 绑定示例:

/* 板级 24MHz 主振荡器 */
osc_24m: oscillator-24m {
    compatible = "fixed-clock";
    #clock-cells = <0>;
    clock-frequency = <24000000>;
    clock-output-names = "osc_24m";
};

该节点定义了一个 24MHz 的固定时钟,输出名称为 "osc_24m"。其他设备(如 UART、I2C 控制器)可以通过 clocks = <&osc_24m>; 直接引用它。

4.2 crystal-clock 绑定规范

crystal-clock 是 fixed-clock 的扩展,专门用于描述晶体振荡器。除了频率信息外,它还包含晶振的负载电容和等效串联电阻等物理参数,帮助 clk 框架更精确地建模时钟源。

属性名 类型 是否必需 描述
compatible string 必需 必须为 "crystal-clock"
#clock-cells u32 必需 固定为 0
clock-frequency u32 必需 晶振标称频率,单位 Hz
load-capacitance-pf u32 可选 负载电容,单位皮法(pF)
series-resistor-ohm u32 可选 等效串联电阻,单位欧姆(Ω)

晶振时钟绑定示例:

/* 32.768kHz 实时时钟晶振 */
rtc_xtal: crystal-32768 {
    compatible = "crystal-clock";
    #clock-cells = <0>;
    clock-frequency = <32768>;
    load-capacitance-pf = <12>;
    series-resistor-ohm = <50000>;
    clock-output-names = "rtc_xtal";
};

实践建议:当晶振的负载电容和串联电阻对系统时钟精度有影响时(如 RTC 或高精度 ADC 时钟),应优先使用 crystal-clock 而非 fixed-clock。内核 clk 框架可以利用这些参数进行温度补偿或频率微调。

4.3 时钟提供者绑定结构图

下图展示了 fixed-clock 和 crystal-clock 在设备树中的位置关系,以及消费者设备如何通过 clocks 属性引用它们:

时钟提供者绑定结构图 / (根节点) osc_24m: oscillator-24m compatible = "fixed-clock" clock-frequency = <24000000> rtc_xtal: crystal-32768 compatible = "crystal-clock" clock-frequency = <32768> uart0: serial@... clocks = <&osc_24m> rtc: rtc@... clocks = <&rtc_xtal> 说明 fixed-clock 和 crystal-clock 都输出单一时钟(#clock-cells = 0),消费者通过 phandle 直接引用。

4.4 时钟提供者绑定对比

下表从多个维度对比 fixed-clock 和 crystal-clock 的差异,帮助开发者在实际项目中做出正确选择:

对比维度 fixed-clock crystal-clock
compatible "fixed-clock" "crystal-clock"
物理参数 仅频率 频率 + 负载电容 + 串联电阻
适用场景 RC 振荡器、外部有源晶振、PLL 输出 无源晶体振荡器(石英晶振)
内核驱动 drivers/clk/clk-fixed-rate.c drivers/clk/clk-fixed-rate.c(扩展支持)
调试信息 仅输出频率 输出频率 + 负载电容 + 电阻
推荐使用 通用固定时钟源 需要精确时钟建模的晶振场景

4.5 多时钟提供者组合示例

在实际板级设备树中,通常同时存在多个时钟提供者。以下是一个完整的组合示例,展示了如何在一个 SoC 平台上同时定义 fixed-clock 和 crystal-clock:

/ {
    /* 主系统时钟:24MHz 有源晶振 */
    clk_24m: clock-24m {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <24000000>;
        clock-output-names = "sys_24m";
    };

    /* RTC 时钟:32.768kHz 无源晶振 */
    clk_32k: clock-32k {
        compatible = "crystal-clock";
        #clock-cells = <0>;
        clock-frequency = <32768>;
        load-capacitance-pf = <12>;
        series-resistor-ohm = <50000>;
        clock-output-names = "rtc_32k";
    };

    /* 以太网 PHY 时钟:25MHz 固定时钟 */
    clk_eth: clock-eth {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <25000000>;
        clock-output-names = "eth_25m";
    };

    /* 消费者设备 */
    uart0: serial@ff000000 {
        compatible = "some-uart";
        reg = <0xff000000 0x1000>;
        clocks = <&clk_24m>;
        clock-names = "uart_clk";
    };

    rtc: rtc@ff010000 {
        compatible = "some-rtc";
        reg = <0xff010000 0x1000>;
        clocks = <&clk_32k>;
        clock-names = "rtc_clk";
    };

    eth: ethernet@ff020000 {
        compatible = "some-eth";
        reg = <0xff020000 0x1000>;
        clocks = <&clk_eth>;
        clock-names = "eth_clk";
    };
};

常见错误:在 fixed-clock 或 crystal-clock 节点中错误地设置了 #clock-cells = <1>。这会导致消费者引用时内核解析失败,因为 clk 框架期望 #clock-cells = <0> 的提供者不携带额外索引参数。如果消费者写成 clocks = <&osc_24m 0>,而提供者声明 #clock-cells = <0>,则编译时会报错。

4.6 调试与验证

在设备树编译和内核启动后,可以通过以下方法验证时钟提供者绑定是否正确:

  • 设备树编译检查:使用 dtc -I dts -O dtb -o output.dtb input.dts 编译,确保无语法错误。
  • 内核启动日志:搜索 clk 相关日志,确认 fixed-clock 和 crystal-clock 节点被正确注册:
    [    0.123456] clk: registering fixed clock: osc_24m
    [    0.123789] clk: registering crystal clock: rtc_xtal
  • clk_summary 接口:通过 /sys/kernel/debug/clk/clk_summary 查看时钟树,确认时钟提供者及其频率:
       clock                         enable_cnt  prepare_cnt  rate
     osc_24m                                  1            1  24000000
     rtc_xtal                                 1            1     32768

调试技巧:如果时钟提供者未出现在 clk_summary 中,首先检查设备树中 compatible 属性是否拼写正确(区分大小写),其次确认内核是否编译了对应的驱动(CONFIG_COMMON_CLK 和 CONFIG_CLK_FIXED_RATE)。

4.7 时钟提供者注册流程

下图展示了内核从解析设备树到注册 fixed-clock 提供者的完整流程:

fixed-clock 内核注册流程 步骤 1 设备树解析 步骤 2 匹配 compatible 步骤 3 分配 clk_hw 步骤 4 设置频率 步骤 5 注册到 clk 框架 步骤 6 添加到时钟树 关键内核函数调用链 of_fixed_clk_setup() → clk_hw_register_fixed_rate() → clk_register() → __clk_create_clk() → of_clk_add_provider() 最终时钟节点出现在 /sys/kernel/debug/clk/ 目录下

理解上述流程有助于在调试时定位问题:如果时钟提供者未出现在 clk_summary 中,可以检查 of_fixed_clk_setup() 是否被调用,以及设备树节点是否被正确匹配。