驱动开发硬核特训 · Day 27(下篇):深入掌握 Common Clock Framework 架构与实战开发

节。


在本篇内容中,我们将围绕 Linux 内核中的时钟子系统核心架构 —— Common Clock Framework(简称 CCF)展开深入讲解,目标是帮助你全面理解其设计理念、主要数据结构、注册流程、驱动实现方式,以及如何基于 NXP i.MX8MP 平台进行完整的时钟驱动开发。


一、Common Clock Framework 简介

Common Clock Framework(CCF)是 Linux 内核自 v3.4 起引入的通用时钟架构,用于统一管理 SoC 上所有的时钟资源,解决平台异构、驱动分裂、代码冗余等问题。

CCF 的设计目标是:

  • 提供统一的时钟抽象接口(如 clk_get() / clk_prepare() / clk_enable())。
  • 支持复杂的时钟树结构,包括分频、门控、复用器等组件。
  • 支持时钟源的动态切换与频率动态调整(DFS)。
  • 解耦设备驱动与时钟控制的实现。

二、CCF 的三大核心角色与架构总览

在上一篇中我们已概述三个核心角色:

角色作用示例
时钟提供者(Provider)注册并实现时钟控制逻辑,如 PLL、分频器、门控器NXP CCM 时钟控制器驱动
时钟消费者(Consumer)使用时钟接口获取和启用时钟UART/I2C 等外设驱动
时钟框架(Framework)统一管理所有时钟,维护拓扑关系与接口drivers/clk/clk.c

架构示意图:

+---------------------+      +-------------------+
| UART 驱动 (Consumer)|<---->|   CCF 框架层       |
+---------------------+      +-------------------+↑|+---------------------+| 时钟控制器驱动       || (如 fsl-imx8mp-ccm)  |+---------------------+

在这里插入图片描述

三、CCF 核心数据结构分析

1. struct clk_hw

该结构体表示一个底层时钟硬件实体,由时钟提供者实现:

struct clk_hw {struct clk_core *core;struct clk_init_data *init;// 可扩展的私有数据指针
};

2. struct clk_ops

用于定义操作时钟的函数集,是最关键的回调接口:

struct clk_ops {int  (*enable)(struct clk_hw *hw);void (*disable)(struct clk_hw *hw);unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate);long (*round_rate)(struct clk_hw *hw, unsigned long rate,unsigned long *parent_rate);int  (*set_rate)(struct clk_hw *hw, unsigned long rate,unsigned long parent_rate);...
};

3. struct clk_init_data

用于注册时钟时的初始化信息:

struct clk_init_data {const char *name;const struct clk_ops *ops;const char * const *parent_names;u8 num_parents;unsigned long flags;
};

四、时钟提供者(Provider)注册流程

Provider 通过 clk_register()devm_clk_hw_register()clk_hw 实例注册给框架,框架内部建立 clk_core 对象并添加至全局时钟树。

示例流程:

  1. 定义 clk_ops 实现;
  2. 构造 clk_hw
  3. 填写 clk_init_data
  4. 调用 devm_clk_hw_register() 完成注册;
  5. 若是 platform 设备,通过 of_clk_add_hw_provider() 提供给设备树接口。

五、实战开发:NXP i.MX8MP 时钟驱动解析

我们以 NXP i.MX8MP 的 UART2 根时钟 IMX8MP_CLK_UART2_ROOT 为例,讲解从时钟控制器提供者,到 UART2 驱动消费者的完整链路。

1. 设备树配置分析

uart2: serial@30890000 {compatible = "fsl,imx8mp-uart", "fsl,imx6q-uart";reg = <0x30890000 0x10000>;interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clk IMX8MP_CLK_UART2_ROOT>,<&clk IMX8MP_CLK_UART2_ROOT>;clock-names = "ipg", "per";status = "disabled";
};

<&clk IMX8MP_CLK_UART2_ROOT> 表示设备节点引用了 ID 为 IMX8MP_CLK_UART2_ROOT 的时钟,provider 必须实现它。

2. Provider 实现(drivers/clk/imx/clk-imx8mp.c)

static const struct imx8m_clk_root_clk uart2_root = {.id = IMX8MP_CLK_UART2_ROOT,.name = "uart2_root_clk",.parent_names = (const char *[]){ "uart2_div" },.num_parents = 1,.flags = CLK_SET_RATE_PARENT,
};

注册流程:

hw = imx8m_clk_hw_register_composite(NULL, clk_root->name,clk_root->parent_names, clk_root->num_parents,mux_hw, &clk_mux_ops,rate_hw, &clk_divider_ops,gate_hw, &clk_gate_ops,clk_root->flags);

注: 每个 root clock 通常由一个复用器、一个分频器和一个门控组成,符合 CCF 的复合时钟模型。


六、时钟消费者如何使用时钟

驱动中使用以下接口获取与控制时钟:

struct clk *clk = devm_clk_get(dev, "ipg");
clk_prepare_enable(clk);
...
clk_disable_unprepare(clk);

内核根据 clock-names 字符串与设备树中的 <&clk ID> 找到实际的 clk_core 实例,进一步调用对应 clk_ops 接口。


七、完整代码实例:模拟一个外设时钟 Provider

以下模拟一个名为 "demo-gate-clk" 的时钟,基于 GPIO 控制实现一个简单门控时钟:

1. 示例平台驱动结构:

static struct clk_hw *demo_clk_hw;
static struct clk_ops demo_clk_ops = {.enable = demo_clk_enable,.disable = demo_clk_disable,
};static int demo_clk_probe(struct platform_device *pdev)
{struct clk_init_data init;demo_clk_hw = devm_kzalloc(&pdev->dev, sizeof(*demo_clk_hw), GFP_KERNEL);init.name = "demo-gate-clk";init.ops = &demo_clk_ops;init.num_parents = 0;init.flags = 0;demo_clk_hw->init = &init;clk_hw_register(&pdev->dev, demo_clk_hw);of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_simple_get, demo_clk_hw);return 0;
}

2. 在设备树中描述该时钟:

demo_clk: demo-clk {compatible = "vendor,demo-gate-clk";#clock-cells = <0>;
};

3. 在其他设备中消费该时钟:

my_peripheral@12340000 {...clocks = <&demo_clk>;clock-names = "core";
};

八、Framework 的统一管理机制

Linux 内核 CCF 框架通过 clk_core 全局对象池,维持整个时钟拓扑结构,并提供统一接口进行操作与调试。

核心文件位于 drivers/clk/clk.c,负责:

  • 初始化时钟树结构;
  • 实现 clk_get()clk_set_rate() 等函数;
  • 管理依赖关系(父子时钟);
  • 调度跨时钟域变更时的同步机制。

框架的调试接口如:

cat /sys/kernel/debug/clk/clk_summary

可查看当前系统所有时钟状态。


九、总结与实战建议

  • Provider 注册逻辑必须按 CCF 规则实现 clk_hwclk_ops,并注册给框架;
  • Consumer 驱动应只依赖标准 clk_*() 接口,不直接操作硬件;
  • Framework 负责维护拓扑、管理父子关系、支持动态调频调占;
  • 实战中,先梳理平台提供的 clock ID 定义(如 imx8mp-clock.h),再定位 clock controller 驱动;
  • 开发自定义外设或 FPGA 时钟控制时,也可编写 Provider 兼容设备树接口。

十、每日提问与答案

问题 1:一个时钟可以有多个父时钟吗?如何选择?

回答:
可以。CCF 支持多父时钟的复用器机制,注册 clk_hw 时传入多个 parent_names,并实现 set_parent()get_parent() 回调。使用者可调用 clk_set_parent() 选择目标父时钟。


问题 2:如何调试某个时钟未启用的问题?

回答:
可通过以下方式定位:

  • cat /sys/kernel/debug/clk/clk_summary 查看时钟是否启用;
  • 查看是否被 Consumer 使用并调用 clk_prepare_enable()
  • 检查 Provider 的 clk_ops.enable() 是否被成功执行;
  • 验证时钟的 parent 是否启用,是否存在 gating 问题。

以上即为 Day 27 下篇全部内容,完整讲解了 CCF 架构设计与实际使用方式。
下一日我们将正式进入子系统与电源管理集成的驱动开发专题。

视频教程请关注 B 站:“嵌入式 Jerry”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78516.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库基础:数据库类型与MySQL特点详解

一、数据库的主要类型 1. 关系型数据库(RDBMS) 特点:基于关系模型,数据以表格形式存储 代表产品:MySQL、Oracle、SQL Server、PostgreSQL 优势:ACID事务支持、强一致性、成熟的SQL标准 适用场景:需要复杂查询和事务支持的场景 2. 非关系型数据库(NoSQL) 文档型数据库:Mo…

49认知干货:产品的生命周期及类型汇总

49章:产品的生命周期与类型划分 宇宙是运动的而非静止的,任何事物亦是如此。只要是存在的事物,便必然存在周期性变化,就像四季更替中的冬日枯树、春日新芽、夏日繁茂与秋日凋零。 这也意味着:事物的发展,离不开周期的更迭与演化,死亡并非终点,而是一种新的循环转变。 …

【2025最新】为什么用ElasticSearch?和传统数据库MySQL与什么区别?

Elasticsearch 深度解析&#xff1a;从原理到实践 一、为什么选择 Elasticsearch&#xff1f; 数据模型 Elasticsearch 是基于文档的搜索引擎&#xff0c;它使用 JSON 文档来存储数据。在 Elasticsearch 中&#xff0c;相关的数据通常存储在同一个文档中&#xff0c;而不是分散…

Docker安装Gitblit(图文教程)

本章教程,使用Docker安装部署Gitblit。 一、Gitblit简介 Gitblit 是一个基于 Java 的 Git 仓库管理工具,主要用于在局域网或小型团队环境中搭建私有 Git 服务器。它提供了一个简单易用的 Web 界面,用于浏览代码、管理仓库和用户权限等。 二、拉取镜像 sudo docker pull git…

nDCG(归一化折损累计增益) 是衡量排序质量的指标,常用于搜索引擎或推荐系统

nDCG&#xff08;归一化折损累计增益&#xff09; 是衡量排序质量的指标&#xff0c;常用于搜索引擎或推荐系统。核心思想是&#xff1a;排名越靠前的高质量结果&#xff0c;对整体评分的贡献越大&#xff0c;但后续结果的贡献会逐渐“打折”。最终通过对比实际排序与理想排序的…

《从线性到二维:CSS Grid与Flex的布局范式革命与差异解析》

在前端开发的广袤宇宙中&#xff0c;CSS布局技术宛如闪耀的星辰&#xff0c;不断革新与演进&#xff0c;为构建绚丽多彩的网页世界提供了坚实的支撑。其中&#xff0c;CSS Grid布局与Flex布局作为两颗璀璨的明星&#xff0c;以其独特的魅力和强大的功能&#xff0c;深受开发者们…

2025年01月03日美蜥(杭州普瑞兼职)一面

目录 vue2 vue3 的区别react 性能优化react 组件传值v-for 和 v-if 的优先级react 中多个接口请求的数据&#xff0c;需要渲染到一个列表上怎么处理百万条数据怎么渲染vue2、vue3 的响应式原理微前端了解吗git 版本控制git mearge 和 git rebase 的区别垂直水平居中react 中实…

【聚类分析】基于copula的风光联合场景生成与缩减

目录 1 主要内容 风光出力场景生成方法 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序方法复现《融合风光出力场景生成的多能互补微网系统优化配置》风光出力场景生成部分&#xff0c;目前大多数研究的是不计风光出力之间的相关性影响&#xff0c;但是地理位置相近…

LeetCode 1128 等价多米诺骨牌对的数量 题解

今天的每日一题&#xff0c;我的思路还是硬做&#xff0c;不如评论区通过状压写的简单&#xff0c;但是答题思路加算法实现是没有问题的&#xff0c;且时间复杂度也是可以通过的&#xff0c;毕竟全是o(n) 那么我就来说一下我的思路&#xff0c;根据dominoes[i] [a, b] 与 domi…

技术部测试规范

简短测试流程&#xff1a; 开发完成 -> 本地自测 -> 测试环境自测 -> 通知测试同事复测 -> 确认无误后上生产 -> 生产环境自测 -> 再次通知测试同事复测 -> 提交产品验收。 当然可以&#xff01;以下是进一步优化后的测试流程规范&#xff0c;特别强调了开…

算法每日一题 | 入门-顺序结构-大象喝水

大象喝水 题目描述 一只大象口渴了&#xff0c;要喝 20 升水才能解渴&#xff0c;但现在只有一个深 h 厘米&#xff0c;底面半径为 r 厘米的小圆桶 &#xff08;h 和 r 都是整数&#xff09;。问大象至少要喝多少桶水才会解渴。 这里我们近似地取圆周率 π 3.14 \pi3.14 π…

Qt中实现工厂模式

在Qt中实现工厂模式可以通过多种方式&#xff0c;具体选择取决于需求和场景。以下是几种常见的实现方法&#xff1a; 1. 简单工厂模式通过一个工厂类根据参数创建不同对象。cppclass Shape {public: virtual void draw() 0; virtual ~Shape() default;};class Circle : publ…

【前端】ES6一本通_划重点_补充面试题

近两天更新完基本内容&#xff0c;后续长期更新&#xff0c;建议关注收藏点赞。 ES6&#xff08;ECMAScript 2015&#xff09;是现代 JavaScript 的基础&#xff0c;在前端面试中非常常见。 本文已汇总的本站笔记 ES6最重要10特性 对象新增 数组新增 异步、生成器 Promise 模块…

初识 iOS 开发中的证书固定

引言 在移动应用安全领域&#xff0c;HTTPS/TLS 是数据传输的第一道防线&#xff0c;但仅依赖系统默认的证书验证仍有被中间人&#xff08;MITM&#xff09;攻击的风险。Certificate Pinning&#xff08;证书固定&#xff09;通过将客户端信任“钉”在指定的服务器证书或公钥上…

单片机的各个种类及其详细介绍

一、按架构分类的深度解析 1. ARM Cortex-M系列 核心优势&#xff1a; 统一架构&#xff1a;ARM生态完善&#xff0c;工具链&#xff08;Keil、IAR、GCC&#xff09;通用。 性能分层&#xff1a;M0&#xff08;低功耗&#xff09;、M3&#xff08;平衡&#xff09;、M4/M7&am…

5.7/Q1,GBD数据库最新文章解读

文章题目&#xff1a;Global, regional, and national burden and trends of rheumatoid arthritis among the elderly population: an analysis based on the 2021 Global Burden of Disease study DOI&#xff1a;10.3389/fimmu.2025.1547763 中文标题&#xff1a;全球、区域…

从微服务到AI服务:Nacos 3.0如何重构下一代动态治理体系?

在现代微服务架构的浪潮中&#xff0c;Nacos早已成为开发者手中的“瑞士军刀”。作为阿里巴巴开源的核心中间件&#xff0c;它通过动态服务发现、统一配置管理和服务治理能力&#xff0c;为云原生应用提供了坚实的基石。从初创公司到全球500强企业&#xff0c;Nacos凭借其开箱即…

Unity与Unreal Engine(UE)的深度解析及高级用法

以下是Unity与Unreal Engine(UE)的深度解析及高级用法对比,结合技术特性、行业应用与未来发展进行综合阐述: 一、核心差异与适用场景对比 1. 技术架构与编程模式 Unity 语言与脚本:主要使用C#,语法简洁且易于学习,适合快速原型开发和中小型项目。支持可视化脚本工具(如…

李沐动手深度学习(pycharm中运行笔记)——05.线性代数

05.线性代数&#xff08;与课程对应&#xff09; 1、导入torch import torch2、 标量由只有一个元素的张量表示 x torch.tensor([3.0]) y torch.tensor([2.0]) print("x y:", x y, "\nx * y:", x * y, "\nx / y:", x / y, "\nx ** y…

Python3与Dubbo3.1通讯解决方案(dubbo-python)

【文章非VIP可读&#xff0c;如果发现阅读限制为系统自动修改阅读权限&#xff0c;请留言我改回】 概述 最近AI项目需要java与python通讯&#xff0c;两边都是比较新的版本。因此需要双方进行通讯&#xff0c;在这里记录一下所采用的方案和关键点。 JAVA调用Python python通…