3、时钟设备树绑定规范:时钟提供者与时钟消费者概念
在设备树中,时钟资源的描述遵循一套严格的绑定规范,将系统中的时钟硬件抽象为两种角色:时钟提供者(Clock Provider)和时钟消费者(Clock Consumer)。这种分层模型使得时钟树的管理、传递和复用变得清晰且可扩展。
核心思想:提供者负责“产生”时钟信号,消费者负责“使用”时钟信号。设备树通过特定的属性将两者连接起来,形成完整的时钟拓扑。
3.1 时钟提供者(Clock Provider)
时钟提供者通常是硬件上能够输出时钟信号的设备,例如:
- 晶体振荡器(Fixed Clock)
- PLL(锁相环)
- 时钟分频器/多路选择器
- SoC 内部的时钟控制器(Clock Controller)
在设备树中,一个时钟提供者节点必须包含以下关键属性:
| 属性 | 含义 | 示例值 |
|---|---|---|
#clock-cells |
指定消费者引用该时钟时需要提供的参数个数 | 0、1、2 |
clock-output-names |
(可选)为该提供者输出的时钟命名 | "pll0", "pll1" |
clock-frequency |
(可选)固定频率时钟直接声明频率值 | 24000000 |
#clock-cells 的含义:
0:该提供者只输出一个时钟,消费者引用时不需要额外参数。1:该提供者输出多个时钟,消费者需要通过一个整数索引来指定要使用哪一个。2:更复杂的场景,可能需要两个参数(如域 + 索引)。
典型示例:固定频率时钟(Fixed Clock)
osc24M: osc24M {
compatible = "fixed-clock";
#clock-cells = <0>; /* 只有一个输出,无需索引 */
clock-frequency = <24000000>; /* 24 MHz */
clock-output-names = "osc24M";
};
典型示例:多输出时钟控制器
clk_controller: clock-controller@10010000 {
compatible = "vendor,clk-controller";
reg = <0x10010000 0x1000>;
#clock-cells = <1>; /* 消费者需提供索引来选择时钟 */
clocks = <&osc24M>; /* 该控制器本身也需要一个输入时钟 */
clock-names = "osc";
};
3.2 时钟消费者(Clock Consumer)
时钟消费者是任何需要时钟信号才能工作的设备,例如:UART、I2C、SPI、MMC、GPU、CPU 等。消费者通过设备树中的 clocks 和 clock-names 属性来声明自己需要哪些时钟。
| 属性 | 含义 | 格式 |
|---|---|---|
clocks |
一个或多个时钟的引用列表 | <&provider_phandle clock_specifier> |
clock-names |
(可选)为每个时钟引用指定一个名称,便于驱动查找 | "name1", "name2" |
消费者示例:UART 设备
uart0: serial@10020000 {
compatible = "vendor,uart";
reg = <0x10020000 0x100>;
interrupts = <0 20 4>;
clocks = <&clk_controller 3>, <&osc24M>; /* 引用两个时钟 */
clock-names = "baud", "core"; /* 分别命名为 baud 和 core */
};
注意事项:
clocks属性中每个条目必须与clock-names中的名称一一对应。- 如果提供者的
#clock-cells为 0,则消费者引用时只需提供 phandle,不需要 specifier。 - 如果提供者的
#clock-cells为 1,则消费者必须提供一个整数作为索引。
3.3 提供者与消费者的连接关系
下图展示了设备树中时钟提供者与消费者之间的典型连接拓扑:
3.4 绑定规范总结
下表总结了提供者与消费者在设备树绑定中的核心差异:
| 角色 | 核心属性 | 职责 | 典型节点 |
|---|---|---|---|
| 提供者 | #clock-cells |
声明输出时钟的数量和索引方式 | 固定时钟、PLL、时钟控制器 |
| 消费者 | clocks、clock-names |
声明需要的时钟引用和名称 | UART、I2C、MMC、GPU |
驱动开发视角:
- 提供者驱动需要实现
of_clk_add_provider()来注册自己的时钟输出。 - 消费者驱动通过
devm_clk_get(dev, "clock-name")来获取对应的时钟句柄。 - 内核的
clk_get()函数会自动解析设备树中的clocks属性,找到对应的提供者并返回时钟结构体。