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 提供者与消费者的连接关系

下图展示了设备树中时钟提供者与消费者之间的典型连接拓扑:

时钟提供者与消费者连接拓扑 时钟提供者与消费者连接拓扑 固定时钟 (osc24M) #clock-cells = <0> 时钟控制器 #clock-cells = <1> UART 消费者 clocks = <&clk 3> I2C 消费者 clocks = <&osc24M> SPI 消费者 clocks = <&clk 5> 输入时钟 clk 输出 (索引 3) 直接引用 osc24M clk 输出 (索引 5) 说明:消费者通过 phandle 引用提供者,并根据 #clock-cells 提供索引参数,形成完整的时钟依赖链。

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 属性,找到对应的提供者并返回时钟结构体。