4. 时钟框架入门:Linux Common Clock Framework (CCF)
在骁龙平台 MIPI CSI 调试中,时钟是保证数据链路同步的基础。Linux 内核提供了 Common Clock Framework (CCF) 来统一管理系统中所有的时钟资源。理解 CCF 的 provider/consumer 模型以及设备树中的时钟描述方法,是正确配置 CSI 接口时钟的前提。
4.1 CCF 核心概念:Provider 与 Consumer
CCF 将系统中的时钟设备抽象为两类角色:
- Clock Provider(时钟提供者):能够产生或转发时钟信号的硬件模块,例如 SoC 内部的 PLL、晶振振荡器、时钟门控单元等。在驱动中,它们注册为
struct clk_hw并提供操作函数。 - Clock Consumer(时钟消费者):需要使用时钟信号才能工作的硬件模块,例如 MIPI CSI 控制器、I2C 控制器、GPU 等。它们通过 CCF 的 API 获取并控制时钟频率。
调试要点: 在 MIPI CSI 链路中,Sensor 是时钟消费者,而 SoC 的 CSI PHY 或 Digital Core 通常也是消费者。真正的时钟提供者往往是 SoC 内部的 PLL 或外部晶振。务必在设备树中明确这种供需关系。
4.2 设备树中的时钟描述:clock-cells 与 clocks
设备树通过两个关键属性来描述时钟的拓扑结构:
| 属性 | 所属节点 | 作用 | 典型值 |
|---|---|---|---|
#clock-cells |
Clock Provider | 定义该时钟控制器输出的时钟索引数量。0 表示单输出,1 表示多输出。 | <0> 或 <1> |
clocks |
Clock Consumer | 引用该设备所需的时钟源,格式为 <phandle clock-specifier>。 |
<&gcc GCC_CAMSS_MCLK0_CLK> |
clock-names |
Clock Consumer | 为 clocks 属性中的每个时钟指定名称,便于驱动查找。 |
"camss_mclk" |
assigned-clocks |
任意节点 | 在系统启动时强制将某个时钟设置为特定频率或父时钟。 | <&gcc GCC_CAMSS_MCLK0_CLK> |
关键规则: 一个节点不能同时作为 Provider 和 Consumer 使用同一个
#clock-cells 属性。Provider 节点用 #clock-cells 声明输出端口数量,Consumer 节点用 clocks 引用输入端口。
4.3 典型设备树时钟配置示例
以下是一个骁龙平台 Camera Sensor 节点与 GCC(Global Clock Controller)的时钟绑定示例:
// Clock Provider: GCC 控制器
gcc: clock-controller@100000 {
compatible = "qcom,gcc-sdm845";
reg = <0x0100000 0x200000>;
#clock-cells = <1>; // 多输出,通过索引区分
#reset-cells = <1>;
#power-domain-cells = <1>;
};
// Clock Consumer: Camera Sensor
i2c0: i2c@c170000 {
status = "okay";
camera_sensor: camera@10 {
compatible = "sony,imx577";
reg = <0x1a>;
clocks = <&gcc GCC_CAMSS_MCLK0_CLK>;
clock-names = "camss_mclk";
assigned-clocks = <&gcc GCC_CAMSS_MCLK0_CLK>;
assigned-clock-rates = <24000000>; // 24 MHz
// ... 其他属性
};
};
在这个例子中:
gcc节点通过#clock-cells = <1>声明它可以输出多个时钟,每个时钟由一个整数索引标识。camera_sensor节点通过clocks属性引用 GCC 输出的GCC_CAMSS_MCLK0_CLK时钟。assigned-clock-rates强制该时钟在启动时初始化为 24 MHz,这是 MIPI CSI 主时钟的常见频率。
4.4 驱动侧时钟操作流程
在 Camera 驱动中,典型的时钟操作步骤如下:
- 获取时钟句柄:
struct clk *mclk = devm_clk_get(dev, "camss_mclk"); - 准备时钟:
clk_prepare(mclk);(可能睡眠) - 设置频率:
clk_set_rate(mclk, 24000000); - 使能时钟:
clk_enable(mclk);(原子操作) - 使用时钟:MIPI CSI 开始传输数据。
- 禁用时钟:
clk_disable(mclk); - 停止时钟:
clk_unprepare(mclk);
常见错误: 在设备树中忘记添加
assigned-clock-rates 或设置错误频率,导致 Sensor 输出时钟与 SoC 期望不匹配,表现为 MIPI 链路无法锁定或数据错位。调试时务必使用 clk_summary 或 clk_get_rate 确认实际时钟频率。
4.5 调试技巧:验证时钟配置
在系统启动后,可以通过以下方法验证时钟配置是否正确:
| 方法 | 命令/操作 | 预期输出 |
|---|---|---|
| 查看时钟树 | cat /sys/kernel/debug/clk/clk_summary |
显示所有时钟的 enable_count、prepare_count、rate |
| 检查特定时钟 | cat /sys/kernel/debug/clk/gcc_camss_mclk0/clk_rate |
24000000 |
| 驱动日志 | dmesg | grep camera |
显示 clk_get 和 clk_set_rate 是否成功 |
| 设备树验证 | dtc -I fs -O dts /sys/firmware/devicetree/base |
反编译设备树,检查 clocks 属性是否完整 |