3. 时钟节点属性详解
时钟节点是设备树中描述时钟源、时钟控制器及其输出关系的核心节点。正确配置时钟属性是确保外设获得稳定、精确时钟信号的前提。本节将逐一解析 compatible、clocks、clock-names、clock-frequency、assigned-clocks 等关键属性的语义、配置规则及常见错误。
3.1 compatible 属性:时钟驱动匹配的基石
compatible 属性是设备树节点与内核驱动之间建立绑定的关键字符串列表。对于时钟控制器节点,该属性必须精确匹配驱动中定义的 of_device_id 表。
- 格式:
"manufacturer,model"形式的字符串列表,按优先级从高到低排列。 - 作用:内核通过该属性查找对应的时钟驱动,并调用其 probe 函数。
- 常见错误:字符串拼写错误、大小写不匹配、缺少 fallback 兼容项。
示例: 一个典型的系统时钟控制器节点
clk: clock-controller@ff760000 {
compatible = "rockchip,rk3399-cru", "syscon";
reg = <0x0 0xff760000 0x0 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
此处 "rockchip,rk3399-cru" 是主兼容项,"syscon" 作为通用 fallback,允许节点被系统控制器框架识别。
3.2 clocks 与 clock-names 属性:建立时钟依赖关系
这两个属性通常成对出现在外设节点中,用于声明该设备需要使用的输入时钟源。
| 属性 | 类型 | 描述 |
|---|---|---|
clocks |
phandle + clock-specifier 列表 | 指向时钟提供者节点,并指定所需的时钟输出索引 |
clock-names |
字符串列表 | 为每个时钟指定一个逻辑名称,供驱动通过 devm_clk_get(dev, name) 获取 |
最佳实践: 始终为
clocks 属性配齐对应的 clock-names。这不仅能提高代码可读性,还能在时钟顺序发生变化时避免驱动代码的修改。
/* 外设节点示例:UART 控制器 */
uart0: serial@ff180000 {
compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart";
reg = <0x0 0xff180000 0x0 0x100>;
interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk SCLK_UART0>, <&clk PCLK_UART0>;
clock-names = "baudclk", "apb_pclk";
status = "disabled";
};
上述代码中,SCLK_UART0 提供波特率时钟,PCLK_UART0 提供 APB 总线接口时钟。驱动通过名称 "baudclk" 和 "apb_pclk" 分别获取。
3.3 clock-frequency 属性:固定频率声明
该属性用于直接指定时钟频率,单位为 Hz。它通常出现在固定频率时钟发生器节点(如晶振)或不需要动态调频的简单外设节点中。
- 适用场景:晶振节点、无编程接口的时钟源、固定频率外设。
- 注意事项:对于可编程时钟控制器,该属性通常被忽略,频率由驱动通过寄存器配置决定。
/* 24MHz 晶振节点 */
osc24M: oscillator {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>;
clock-output-names = "osc24M";
};
常见陷阱: 在可编程时钟控制器节点中错误地添加
clock-frequency 属性,可能导致驱动初始化时产生频率冲突。该属性应仅用于固定频率时钟源。
3.4 assigned-clocks 及相关属性:时钟父级与频率的静态分配
这组属性用于在系统启动早期、驱动 probe 之前,由时钟框架自动完成时钟的父级选择和频率设置。它们通常放置在时钟控制器节点或使用时钟的外设节点中。
| 属性 | 作用 |
|---|---|
assigned-clocks |
列出需要被配置的时钟输出 phandle 列表 |
assigned-clock-parents |
为上述时钟指定新的父时钟源 |
assigned-clock-rates |
为上述时钟指定目标频率(Hz) |
/* 在时钟控制器节点中配置时钟树 */
cru: clock-controller@ff760000 {
compatible = "rockchip,rk3399-cru";
reg = <0x0 0xff760000 0x0 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
assigned-clocks = <&cru PLL_GPLL>, <&cru PLL_CPLL>;
assigned-clock-rates = <594000000>, <800000000>;
};
上述配置将 GPLL 锁定在 594 MHz,CPLL 锁定在 800 MHz。这些配置在时钟驱动 probe 完成后立即生效,确保后续外设获取到正确的时钟频率。
3.5 关键属性关系总览
下图展示了时钟节点中各属性之间的逻辑关系与数据流向:
3.6 属性配置故障定位速查表
以下表格总结了时钟节点属性配置中常见的故障现象及其根因:
| 故障现象 | 可能原因 | 排查方向 |
|---|---|---|
| 驱动 probe 失败,返回 -ENODEV | compatible 字符串不匹配 |
核对驱动 of_match_table 与设备树字符串 |
| 时钟获取失败,返回 -EPROBE_DEFER | clocks 引用的时钟提供者未就绪 |
检查时钟控制器节点状态与初始化顺序 |
| 外设时钟频率异常 | assigned-clock-rates 未生效或父时钟选择错误 |
检查 assigned-clock-parents 配置与时钟树拓扑 |
| 系统启动时时钟锁定失败 | clock-frequency 与硬件实际频率不符 |
使用示波器或频率计测量晶振输出 |
| 驱动无法通过名称获取时钟 | clock-names 数量或顺序与 clocks 不匹配 |
确保两个列表长度一致,名称拼写正确 |
调试技巧: 使用
clk_summary 调试文件系统接口(通常挂载在 /sys/kernel/debug/clk/clk_summary)可以实时查看时钟树状态、当前频率、父时钟及启用计数,是定位时钟问题的第一利器。