驱动开发硬核特训 · Day 25 (附加篇):从设备树到驱动——深入理解Linux时钟子系统的实战链路


一、前言

在嵌入式Linux开发中,无论是CPU、外设控制器,还是简单的GPIO扩展器,大多数硬件模块都离不开时钟信号的支撑。

时钟子系统(Clock Subsystem),作为Linux内核中基础设施的一部分,为设备提供统一、灵活、可控的时钟管理机制。
然而,时钟子系统的工作方式常常隐藏在设备树配置与驱动框架之下,让初学者难以直观感知。

今天,我们将通过一个完整、清晰、实战化的例子,带你从设备树定义,到内核驱动调用核心API全面理解时钟子系统的核心知识点和实际用途


在这里插入图片描述

二、实战场景介绍

本次实战选取的例子是 —— UART 控制器(串口)

在 i.MX8MP 平台上,UART1 控制器是一个非常典型的时钟消费者。它需要一个稳定的时钟源来驱动波特率生成器,确保数据收发的准确性。


三、设备树中的时钟配置

在设备树中,为每个时钟消费者(比如 UART)指定它需要的时钟源。来看具体的设备树片段(源自 i.MX8MP EVK 板):

&uart1 { /* BT UART */pinctrl-names = "default";pinctrl-0 = <&pinctrl_uart1>;assigned-clocks = <&clk IMX8MP_CLK_UART1>;assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_80M>;uart-has-rtscts;status = "okay";
};

解释

  • assigned-clocks:指定要配置的时钟节点(这里是UART1的根时钟)。
  • assigned-clock-parents:指定该时钟的上游父时钟(这里是系统PLL1输出的80MHz频率)。
  • status = "okay":使能设备。

小结

在设备树中,UART1通过 assigned-clocks 指定了自己依赖的时钟源。
这一步是让内核启动时,能够正确配置 UART 所需时钟。


四、时钟子系统在内核中的处理流程

内核解析设备树时,会执行以下步骤:

  1. 解析 assigned-clocks 属性

    • 调用 of_clk_get() 获取时钟phandle。
  2. 设置父时钟(if assigned-clock-parents)

    • 调用 clk_set_parent()
  3. 设置频率(if assigned-clock-rates)

    • 调用 clk_set_rate()
  4. 将时钟与设备绑定

    • 供后续驱动使用 clk_get() 提取并控制时钟。

总结一句话:内核在启动时,根据设备树,把设备需要的时钟准备好,驱动层可以直接使用。


五、驱动中的核心函数调用链

进入驱动注册时,通常在 probe() 函数中,设备驱动需要主动申请并启用时钟。

来看典型的 Linux 串口驱动核心代码(以drivers/tty/serial/fsl_lpuart.c为例):

static int lpuart_probe(struct platform_device *pdev)
{struct resource *res;struct clk *clk;struct lpuart_port *sport;// ... 省略其他初始化代码clk = devm_clk_get(&pdev->dev, NULL);if (IS_ERR(clk))return PTR_ERR(clk);clk_prepare_enable(clk);// 后续设置UART寄存器,开始工作return 0;
}

解释

  • devm_clk_get(&pdev->dev, NULL):从时钟子系统中获取设备绑定的时钟(根据设备树内容解析)。
  • clk_prepare_enable(clk):准备并使能时钟,确保模块时钟打开,才能访问寄存器或工作。

这两个函数是驱动中最核心的标准时钟处理流程


六、核心知识点总结

过程内容说明
设备树定义通过 assigned-clocks assigned-clock-parents指定设备需要使用的时钟及父时钟关系
内核解析设备树使用 of_clk_get()clk_set_parent() 等接口完成设备初步时钟配置
驱动中申请时钟使用 devm_clk_get() 获取时钟句柄与设备生命周期绑定,自动管理释放
启用时钟使用 clk_prepare_enable()确保时钟开启,模块能够正常读写工作
关闭时钟使用 clk_disable_unprepare()(通常在 remove 时)释放功耗资源,避免悬空开启

七、完整链路小结(可视化)

[设备树]|v
解析 assigned-clocks → 绑定 clk_hw → 初始化频率和父时钟|v
驱动 probe 阶段|v
devm_clk_get() 获取时钟|v
clk_prepare_enable() 打开时钟|v
模块正常运行

八、实战小问答(加深记忆)

  • Q:如果不调用 clk_prepare_enable() 会怎么样?
    ➔ A:设备可能无法正常访问寄存器(时钟门控),导致挂死或者硬件超时错误。

  • Q:为什么用 devm_clk_get()
    ➔ A:简化驱动资源管理,避免忘记释放时钟资源导致内存泄漏。

  • Q:时钟源一定是固定的吗?
    ➔ A:不一定,有些时钟(如CPU频率)是动态可变的,需要动态控制频率。

九、driver/clk/子系统的核心组成

在 Linux 内核中,driver/clk/ 目录下是整个 Common Clock Framework(CCF) 的具体实现。

它包含两大部分:

部分作用
核心框架代码(Generic)提供统一时钟注册、获取、启用、频率调整等API
平台时钟驱动(Platform-specific)各芯片平台的具体时钟树实现,比如 imx8mp

十、实战中最关键的核心文件和功能

我们只讲跟设备树解析、驱动使用真正相关的代码,不浪费篇幅。

目录/文件作用备注
drivers/clk/clk.c核心通用时钟框架逻辑所有 clk_register, clk_get, clk_prepare_enable 都在这里最终处理
drivers/clk/clk-fixed-rate.c实现固定频率的时钟(如设备树里的 fixed-clock)常用于 camera、USB、PCIE refclk
drivers/clk/imx/clk-imx8mp.ci.MX8MP 平台特有的时钟树搭建把 IMX8MP 平台的所有时钟源、分频器、复用器建立起来

十一、真正重要的核心函数讲解(来自 driver/clk/clk.c)

11.1 clk_register()

🔵 作用:
在内核启动时,或者平台驱动初始化时,把一个新的时钟节点注册到内核的时钟树中。

🔵 主要流程:

  • struct clk_hw 转换为 struct clk_core
  • clk_core 挂到内核全局时钟链表
  • 处理时钟父子关系

🔵 关键源码位置:

struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{struct clk_core *core;...core = kzalloc(sizeof(*core), GFP_KERNEL);core->hw = hw;...hlist_add_head(&core->node, &clk_root_list);return __clk_create_clk(core, NULL);
}

这里的 clk_core 是真正管理时钟状态的内部结构。


11.2 of_clk_get()devm_clk_get()

🔵 作用:

  • of_clk_get():从设备树 phandle 提取时钟节点。
  • devm_clk_get():在驱动中调用,帮你自动结合生命周期管理。

🔵 主要流程:

  • 根据设备树 phandle 查找 clk_provider
  • 通过 provider的 of_clk_src_onecell_get() 或类似接口拿到 clk

🔵 关键源码位置:

struct clk *of_clk_get(struct device_node *np, int index)
{...provider = of_parse_phandle(np, "clocks", index);...return provider->get(provider->data, index);
}

最终就是在驱动层 devm_clk_get() 里面,调用 of_clk_get(),并返回一个 clk 结构体给设备驱动用。


11.3 clk_prepare_enable()clk_disable_unprepare()

🔵 作用:

  • clk_prepare_enable():打开时钟,并确保硬件模块有时钟供给。
  • clk_disable_unprepare():关闭时钟,释放资源。

🔵 核心调用逻辑:

int clk_prepare_enable(struct clk *clk)
{int ret;ret = clk_prepare(clk);if (ret)return ret;ret = clk_enable(clk);if (ret)clk_unprepare(clk);return ret;
}

本质就是调用底层时钟驱动注册时定义好的 enable/disable 函数指针。

比如:

  • PLL的 enable
  • gate clock 的开关
  • mux clock 的切换

十二、fixed-clock 举例(drivers/clk/clk-fixed-rate.c)

在设备树中,如果定义了:

pcie0_refclk: pcie0-refclk {compatible = "fixed-clock";#clock-cells = <0>;clock-frequency = <100000000>;
};

内核会自动匹配到 drivers/clk/clk-fixed-rate.c 中的 of_fixed_clk_setup()

static void __init of_fixed_clk_setup(struct device_node *node)
{struct clk_fixed_rate *fixed;...fixed->hw.init = &init;clk_register(NULL, &fixed->hw);
}

➔ 最后也是通过 clk_register() 注册到内核时钟树。

所以:

  • fixed-clock → 固定频率的时钟节点
  • 设备(比如 PCIe PHY)可以直接引用它做为参考时钟

✨ 最重要总结(一句话)

✅ 无论是外设驱动,还是内核框架,
✅ 设备树 → of_clk_get → clk_register → clk_prepare_enable
✅ 整个链路最终就是围绕 clk_coreclk_ops 把时钟管理起来,
✅ 保证所有设备能统一管理时钟、动态调频、动态开关。


📋 最后再给你一张核心流程小图(助记忆)

[设备树 clocks属性]↓
[of_clk_get()]↓
[clk_register() 把hw挂到core]↓
[devm_clk_get()]↓
[clk_prepare_enable()]↓
[设备驱动正常工作]

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

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

相关文章

并发设计模式实战系列(7):Thread Local Storage (TLS)

&#x1f31f; 大家好&#xff0c;我是摘星&#xff01; &#x1f31f; 今天为大家带来的是并发设计模式实战系列&#xff0c;第七章Thread Local Storage (TLS)&#xff0c;废话不多说直接开始~ 目录 一、核心原理深度拆解 1. TLS内存模型 2. 关键特性 二、生活化类比&a…

时序数据库 TDengine × Perspective:你需要的可视化“加速器”

你有没有遇到这样的场景&#xff1a;数据已经写进数据库&#xff0c;图表却总是“慢半拍”&#xff1f;或是操作界面太卡&#xff0c;光是一个排序就能让你等到喝完一杯咖啡&#xff1f;当数据量越来越大、响应时间却越来越长&#xff0c;开发者和用户都不禁要问一句——就没有…

前端面试每日三题 - Day 19

这是我为准备前端/全栈开发工程师面试整理的第十一天每日三题练习&#xff0c;涵盖 JavaScript中WeakMap与内存管理的底层机制、Redux Toolkit的事件以及系统设计中的企业级表单引擎构建。通过这三道题&#xff0c;你将对现代前端开发中的关键概念有更深入的理解&#xff0c;并…

Antd Modal Drawer 更改默认项

当项目比较大使用了非常多的 Modal 和 Drawer 要是有需求一次性全部调整就会比较麻烦&#xff0c;目前 Antd 的 ConfigProvider 暂不支持&#xff08;也有可能我没找到&#xff0c;待大佬指证&#xff09;就比如由于默认 Modal Drawer 的遮罩层是可以点击关闭的&#xff0c;但是…

硬件工程师面试常见问题(8)

第三十六问&#xff1a;基尔霍夫定理的内容是什么&#xff1f; 基尔霍夫电流定理&#xff1a; 1. 内容&#xff1a;电路中任意一个节点上&#xff0c;在任意时刻&#xff0c;流入节电的电流之和等于流出节点的电流之和。 2. 表达式&#xff1a;根据上图写出节点电流定律的数学…

Elasticsearch 内存使用指南

作者&#xff1a;来自 Elastic Valentin Crettaz 探索 Elasticsearch 的内存需求以及不同类型的内存统计信息。 Elasticsearch 拥有丰富的新功能&#xff0c;帮助你为你的使用场景构建最佳搜索解决方案。浏览我们的示例笔记本了解更多信息&#xff0c;开始免费云试用&#xff0…

硬件工程师面试常见问题(9)

第四十一问&#xff1a;色环电阻的颜色表示什么&#xff1f; 各环表示的意思&#xff1a; 4色环的&#xff1a;前两位表示有效位&#xff1b;第三环表示倍乘&#xff1b;最后一环表示误差&#xff1b; 5色环的&#xff1a;前三位表示有效位&#xff1b;第四环表示倍乘&#…

PyTorch 深度学习实战(23):多任务强化学习(Multi-Task RL)之扩展

之前的PyTorch 深度学习实战&#xff08;23&#xff09;&#xff1a;多任务强化学习&#xff08;Multi-Task RL)总结扩展运用代码如下&#xff1a; import torch import torch.nn as nn import torch.optim as optim import numpy as np from torch.distributions import Norm…

前端——CSS1

一&#xff0c;概述 CSS&#xff08;Cascading Style Sheets&#xff09;&#xff08;级联样式表&#xff09; css是一种样式表语言&#xff0c;为html标签修饰定义外观&#xff0c;分工不同 涉及&#xff1a;对网页的文字、背景、宽、高、布局进行修饰 分为内嵌样式表&…

赋能航天教育:高校卫星仿真教学实验平台解决方案

​​​​​​ 随着全球航天事业的飞速发展&#xff0c;对高素质航天人才的需求日益增长。如何在高校阶段提前锻炼学生的航天工程实践能力&#xff0c;成为教育界的重要命题。作为领先的通信与网络技术供应商&#xff0c;IPLOOK基于自身在5G核心网、卫星通信及仿真平台领域的深…

Python爬虫(10)Python数据存储实战:基于pymongo的MongoDB开发深度指南

目录 一、为什么需要文档型数据库&#xff1f;1.1 数据存储的范式变革1.2 pymongo的核心优势 二、pymongo核心操作全解析2.1 环境准备2.2 数据库连接与CRUD操作2.3 聚合管道实战2.4 分批次插入百万级数据&#xff08;进阶&#xff09;2.5 分批次插入百万级数据&#xff08;进阶…

Springboot 手搓 后端 滑块验证码生成

目录 一、效果演示 二、后端滑块验证码生成思路 三、原理解析 四、核心代码拿走 滑块验证码react前端实现&#xff0c;见我的这篇博客&#xff1a;前端 React 弹窗式 滑动验证码实现_react中使用阿里云滑块验证码2.0前端接入及相关视觉-CSDN博客 一、效果演示 生成的案例…

关于flink两阶段提交高并发下程序卡住问题

先抛出代码 package com.dpf.flink;import com.dpf.flink.sink.MysqlSink; import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.…

html css js网页制作成品——HTML+CSS+js美甲店网页设计(5页)附源码

美甲店 目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&a…

LeetCode[347]前K个高频元素

思路&#xff1a; 使用小顶堆&#xff0c;最小的元素都出去了&#xff0c;省的就是大&#xff0c;高频的元素了&#xff0c;所以要维护一个小顶堆&#xff0c;使用map存元素高频变化&#xff0c;map存堆里&#xff0c;然后输出堆的东西就行了 代码&#xff1a; class Solution…

2024年网站开发语言选择指南:PHP/Java/Node.js/Python如何选型?

2024年网站开发语言选择指南&#xff1a;PHP/Java/Node.js/Python如何选型&#xff1f; 一、8大主流Web开发语言技术对比 1. PHP开发&#xff1a;中小型网站的首选方案 最新版本&#xff1a;PHP 8.3&#xff08;2023年11月发布&#xff09;核心优势&#xff1a; 全球78%的网站…

从数据结构说起(一)

1 揭开数据结构神奇的面纱 1.1 初识数据结构 在C的标准库模板&#xff08;Standard Template Library,STL&#xff09;课程上&#xff0c;我初次结识了《数据结构》。C语言提供的标准库模板是面向对象程序设计与泛型程序设计思想相结合的典范。所谓的泛型编程就是编写不依赖于具…

JAVA--- 关键字static

之前我们学习了JAVA 面向对象的一些基本知识&#xff0c;今天来进阶一下&#xff01;&#xff01;&#xff01; static关键字 static表示静态&#xff0c;是JAVA中的一个修饰符&#xff0c;可以修饰成员方法&#xff0c;成员变量&#xff0c;可用于修饰类的成员&#xff08;变…

4.27比赛总结

文章目录 T1T2法一&#xff1a;倍增求 LCA法二&#xff1a;Dijkstra 求最短路法三&#xff1a;dfs 求深度 T3T4总结 T1 一道非常简单的题&#xff0c;结果我因为一句话没写挂了 80pts…… 题目中没写 a a a 数组要按照 b b b 数组的顺序&#xff0c;所以对于最大方案&#x…

数据一致性巡检总结:基于分桶采样的设计与实现

数据一致性巡检总结&#xff1a;基于分桶采样的设计与实现 背景 在分布式系统中&#xff0c;缓存&#xff08;如 Redis&#xff09;与数据库&#xff08;如 MySQL&#xff09;之间的数据一致性问题是一个常见的挑战。由于缓存的引入&#xff0c;数据在缓存和数据库之间可能存…