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 属性引用它们:
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 提供者的完整流程:
理解上述流程有助于在调试时定位问题:如果时钟提供者未出现在 clk_summary 中,可以检查 of_fixed_clk_setup() 是否被调用,以及设备树节点是否被正确匹配。