ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RT-Thread `fixed-clock` 驱动详解:恒定频率时钟提供器的设备树配置、探针流程与消费方用法

2026/10/4 1:40:40 拓冰建站 浏览量
RT-Thread `fixed-clock` 驱动详解:恒定频率时钟提供器的设备树配置、探针流程与消费方用法 操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载导读fixed-clock是 RT-Thread CLKCommon Clock Framework通用时钟框架子系统中最基础、也是几乎所有 SoC 时钟树根节点都会用到的平台驱动它向系统注册一个频率恒定、常开always-on、无 gate、无父时钟、不可编程的时钟单元通常作为晶振crystal、RC 振荡器或外部时钟输入在设备树中的建模方式是 PLL 等复杂时钟控制器CCM的挂载底座。本文以 fixed.md 为核心骨架结合 clk-fixed-rate.c、clk.h 及仓库内真实 DTS 片段完整讲解该驱动的设备树描述、probe 流程、数据结构、OFW 匹配、与 SoC CCM 驱动的选型边界、消费方 API 用法及常见坑位使读者能够正确地在 RT-Thread DMDevice Model项目中声明并消费fixed-clock节点。1. 驱动定位与总体概述fixed-clock平台驱动的实现位于components/drivers/clk/clk-fixed-rate.c驱动名为clk-fixed-rate属于 RT-Thread CLK 子系统总览见 clk.md 的page_device_clk文档。从代码看它注册的时钟具有如下特征恒频频率在设备树clock-frequency属性中写死运行时不变常开实现中没有enable/disable/prepare/unprepare回调——在 ops 层面使能操作是空操作no-op因此它天然适合描述始终运行的根时钟无父无 gate没有父时钟、没有 gate 位、没有 MMIO 可编程寄存器只有唯一的.recalc_rate回调返回固定频率。用一句话概括它在时钟树中的位置fixed-clock是 SoC 时钟树里 PLL 等可编程时钟控制器CCM所“悬挂”的最底层叶子节点。几乎所有带晶振输入的平台设备树根部都会出现一个compatible fixed-clock的节点。2. 设备树描述Provider 与 Consumer2.1 Provider 节点声明固定频率时钟文档给出的标准 Provider 声明如下osc24m: fixed-clock { compatible fixed-clock; #clock-cells 0; clock-frequency 24000000; clock-accuracy 0; /* optional, ppb */ clock-output-names osc24m; /* optional; becomes cell name */ };各属性含义属性必填作用compatible必填固定为fixed-clock用于匹配fixed_clk_ofw_ids#clock-cells必填必须为0表示该 provider 的每个时钟说明符不携带额外整数clock-frequency必填时钟频率单位 Hz缺失将导致 probe 失败clock-accuracy可选时钟精度单位 ppb十亿分之一默认 0clock-output-names可选输出时钟名会成为 cell 的name供rt_clk_get_by_name回退查找使用仓库内真实的 DTS 用法可对照 bsp/qemu-virt64-aarch64/amp.dts 中的clk24m节点clk24m: apb-pclk { clock-output-names clk24mhz; clock-frequency 24000000; #clock-cells 0; compatible fixed-clock; };该节点被同文件中的pl0119040000UART串口节点消费见下文。此外 bsp/zynqmp-a53-dfzu2eg/zynqmp.dts、bsp/raspberry-pi/dm/dts/bcm2711-rpi-4-b.dts、bsp/raspberry-pi/dm/dts/bcm283x-rpi-3-b.dts 中也都有fixed-clock节点可作为多平台参考。2.2 Consumer 节点消费固定频率时钟下游设备通过clocks属性引用 provideruart0: serial40000000 { clocks osc24m; /* clock-names optional when only one clock */ };关键语义当#clock-cells 0时clocks osc24m中的说明符不再携带额外整数内核将隐式按索引 0 解析即rt_ofw_get_clk(np, 0)默认使用args-args[0]无参数时为 0作为 cell 索引。对照 amp.dts 中的真实消费方式pl0119040000 { clock-names uartclk, apb_pclk; clocks clk24m clk24m; ... };注意这里同一根clk24m被同一个节点以两个 clock-names 各引用了一次——固定时钟只有一个 cellcells_nr 1多次引用解析到的都是同一个struct rt_clk_cell框架的引用计数prepare_count/enable_count按 cell 共享因此重复引用不会造成计数错乱。3. Probe 流程源码级拆解文档将 probe 流程概括为 6 步下面逐一对齐源码clk-fixed-rate.c3.1 早期注册INIT_SUBSYS_EXPORTstatic int fixed_clk_drv_register(void) { rt_platform_driver_register(fixed_clk_driver); return 0; } INIT_SUBSYS_EXPORT(fixed_clk_drv_register);通过INIT_SUBSYS_EXPORT在子系统初始化阶段完成rt_platform_driver_register保证固定时钟的 probe 早于 CCM如rockchip,*-cru及其消费方的 phandle 解析让晶振节点在 CCM 消费之前就已注册完毕。3.2 分配与解析fixed_clk_probestatic rt_err_t fixed_clk_probe(struct rt_platform_device *pdev) { rt_err_t err; rt_uint32_t val; struct rt_device *dev pdev-parent; struct clk_fixed *cf rt_calloc(1, sizeof(*cf)); ... if ((err rt_dm_dev_prop_read_u32(dev, clock-frequency, val))) { goto _fail; } cf-fcell.fixed_rate val; val 0; rt_dm_dev_prop_read_u32(dev, clock-accuracy, val); cf-fcell.fixed_accuracy val; rt_dm_dev_prop_read_string(dev, clock-output-names, cf-fcell.cell.name); ... if ((err rt_clk_register(cf-parent))) { goto _fail; } return RT_EOK; _fail: rt_free(cf); return err; }第 3 步rt_dm_dev_prop_read_u32(dev, clock-frequency, val)是必填解析返回值非RT_EOK直接goto _fail释放内存返回错误——文档明确probe 缺少clock-frequency即失败第 4 步clock-accuracy与clock-output-names均为可选解析clock-output-names读取成功后赋给cf-fcell.cell.name成为该 cell 的名字第 5 步接线cells[0]挂载fixed_clk_ops仅含.recalc_rate第 6 步rt_clk_register(cf-parent)将 provider 节点登记进全局时钟节点链表RT_CLK_NODE_OBJ_NAME CLKNP并挂上rt_ofw_data供 phandle 查找若该 fixed-clock 节点自身带assigned-clocks*属性对叶子晶振而言很少见rt_clk_register内部可能调用rt_ofw_clk_set_defaults应用默认配置。失败路径统一rt_free(cf)并返回错误码不留内存泄漏。4. 数据结构clk_fixed与rt_clk_fixed_rate文档给出的结构定义与 clk.h 中的struct rt_clk_fixed_rate完全一致struct clk_fixed { struct rt_clk_node parent; struct rt_clk_fixed_rate fcell; struct rt_clk_cell *cells[1]; }; struct rt_clk_fixed_rate { struct rt_clk_cell cell; rt_ubase_t fixed_rate; rt_ubase_t fixed_accuracy; };字段角色对照成员作用fixed_rate频率Hz来自 DTclock-frequencyfixed_accuracy精度ppb来自 DTclock-accuracy可选cell.ops-recalc_rate直接返回fixed_rate忽略 parent_rate 参数注意struct clk_fixed以rt_clk_node parent开头、rt_clk_fixed_rate fcell内嵌于其中cells[1]只挂一个输出 cell——整个驱动用一次rt_calloc分配全部所需内存结构紧凑、无二次分配。fixed_clk_recalc_rate的实现也印证了“忽略父时钟”的说法static rt_ubase_t fixed_clk_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) { struct rt_clk_fixed_rate *fr rt_container_of(cell, struct rt_clk_fixed_rate, cell); return fr-fixed_rate; }rt_clk_fixed_rate在 clk.h 中的注释也明确其定位Used for constant-frequency clocks without configurable parents or dividers.4.1 关于 enable/prepare 的语义由于fixed_clk_ops中没有.enable/.disable/.prepare/.unprepare使能操作在 ops 层面是空操作。但框架层的引用计数仍然对消费方生效rt_clk_enable会增加 cell 的enable_countrt_clk_disable会递减——list_clk调试命令里依然能看到这些计数。这意味着消费方可以安全地对固定时钟调用rt_clk_prepare_enable/rt_clk_disable_unprepare只是底层没有硬件动作纯粹是引用计数记账。5. OFW 匹配与 Linux 兼容性static const struct rt_ofw_node_id fixed_clk_ofw_ids[] { { .compatible fixed-clock }, { /* sentinel */ } }; static struct rt_platform_driver fixed_clk_driver { .name clk-fixed-rate, .ids fixed_clk_ofw_ids, .probe fixed_clk_probe, };驱动以fixed-clock作为唯一 compatible 匹配项。该绑定与 Linux 内核的 fixed-clock 绑定兼容文档明确说明 Linux-compatible binding因此同一段 DTS 片段既可用于上游 dt-bindings 生态也可直接用于 RT-Thread BSP这是 RT-Thread CLK 框架“镜像 Linux 通用时钟模型以便复用 dt-bindings 与 DTS 片段”设计目标的具体体现见 clk.md。6. 选型边界fixed-clockvs SoC CCM 驱动文档给出了清晰的选择对照表这是实战中最容易混淆的决策点使用fixed-clock使用 SoC 驱动如rockchip,*-cru晶振CrystalPLLRC 振荡器mux多路选择器外部时钟输入divider分频器频率在启动时固定gate门控无 MMIO或 MMIO 不由本驱动管理DVFS、动态 reparent换父——大型时钟控制器 IP判断要点可归结为三条频率是否固定不变——需要 DVFS/动态调频的就交给 CCM是否只是被动输入——晶振、RC 振荡器、外部时钟输入是“源”没有可编程性是否有 MMIO 可编程寄存器——fixed-clock完全没有寄存器操作任何需要写寄存器选频、分频、门控的场景都应交给 SoC 驱动。clk.md中的 CLK 子系统内置 provider 表也印证了这一分层clk-fixed-rate.c 与clk-scmi.c是内核自带的两类通用 provider而 Rockchip 等 SoC 的 CCM/PLL/mux/gate 则通过SOC_DM_CLK_DIR挂在bsp/*/dm/clk/下如 bsp/rockchip/dm/clk/。7. 消费方 API 用法文档给出的消费示例struct rt_clk *clk rt_clk_get_by_name(dev, uart_clk); /* or index 0 if no clock-names */ rt_err_t err rt_clk_prepare_enable(clk); rt_ubase_t hz rt_clk_get_rate(clk);结合 clk.h 与 clk.md 的消费 API 表要点如下rt_clk_get_by_name(dev, uart_clk)按clock-names匹配若 DT 中只有一个时钟且未写clock-names可用rt_clk_get_by_index(dev, 0)其内部按clocks数组序号解析若名字在clock-names中未命中rt_clk_get_by_name还会回退扫描已注册 cell 的cell-name——这正是clock-output-names的作用让固定时钟的 cell 有一个全局唯一、可被名字查找的名字rt_clk_prepare_enable是 probe 中最常用的组合先 prepare 再 enablert_clk_get_rate读取 cell 的速率固定时钟的.recalc_rate直接返回fixed_rate因此读到的就是 DT 里clock-frequency写死的值fixed-clock几乎不需要rt_clk_set_rate——速率固定不可编程若调用框架会走set_rate回调而固定时钟没有实现该回调只会得到“不支持”的语义。7.1 NULL 时钟的便利性rt_clk_enable(NULL)与rt_clk_prepare(NULL)均返回RT_EOK——框架对 NULL 句柄宽容处理方便驱动把fixed-clock当作“可选时钟”使用。但文档也提醒rt_clk_get_*的返回值仍需在启用前判空不能依赖宽容行为掩盖 phandle 解析失败。8. 常见陷阱Pitfalls文档列举了四类高频问题这里逐条展开缺少clock-frequencyprobe 必读该属性失败即返回错误fixed_clk_probe中goto _failprovider 未注册消费方 phandle 解析得到 NULL——rt_clk_get_*返回 NULL后续使能直接静默失败或空指针。DT 中频率填错固定时钟是所有派生 PLL 数学计算的基准写错 Hz 会导致整条时钟链全部算错且是“静默”错误——UART 波特率偏差、SDIO 时序错乱等表现都是下游症状排查时非常隐蔽。建议对晶振频率值做注释、与硬件原理图核对。把需要门控的外设时钟建模成fixed-clockgate 类时钟应由专门的 gate 驱动 cell 提供配合RT_CLK_F_SET_RATE_GATE等 flag见 clk.md 的rt_clk_cellflags 表否则外设将永远“开着”功耗与 DVFS 完全失控。名字冲突clock-output-names应保证足够唯一因为rt_clk_get_by_name存在按cell-name的全局回退查找——重名可能导致消费方拿到错误的时钟句柄。另可补充一条来自 CLK 框架总文档的通用注意点ISR 上下文中禁止调用prepare/prepare_enableRT_DEBUG_NOT_IN_INTERRUPT检查固定时钟虽无硬件准备动作但框架语义上仍属“可睡眠”路径应在线程上下文调用。9. 调试与验证启用RT_USING_CONSOLERT_USING_MSH后可在 MSH shell 中执行list_clk该命令会列出每个已注册 cell 的名称、enable/prepare 计数、速率、消费方dev_id/con_id、父时钟名。对于fixed-clock你会看到速率恒等于 DT 中的clock-frequencyenable/prepare 计数随消费方的启停增减从而直观验证引用计数语义与注册是否成功。10. 总结RT-Thread 的fixed-clock驱动是 CLK 子系统中最简单却也最关键的 provider它以约 100 行代码、一次内存分配、一个.recalc_rate回调为整个 SoC 时钟树提供了恒频常开的根节点并通过与 Linux 兼容的 DT 绑定直接复用上游设备树生态。使用时牢记三条主线DT 中clock-frequency必须准确且必填只有真正的“源时钟”才适合它gate/PLL/mux/divider 一律交给 SoC CCM 驱动消费方统一走rt_clk_get_*rt_clk_prepare_enable的框架 API让引用计数和上层 DVFS 协调逻辑保持完整。进一步阅读CLK 框架总览clk.md驱动实现clk-fixed-rate.c公共头文件与全部 APIclk.h真实 DTS 参考bsp/qemu-virt64-aarch64/amp.dts赞分享操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载相关推荐RT-Thread RTC 设备驱动框架详解时间设置、date 命令与 Soft RTC 使用指南RT Thread RTC 设备驱动框架详解时间设置、date 命令与 Soft RTC 使用指南 RT Thread 的 RTC实时时钟设备为操作系统的操作系统嵌入式物联网嵌入式OSRTOSRT-Thread Nuvoton N9H30 BSP 外设驱动移植详解设备模型、设备名与 Kconfig 配置全解析RT Thread Nuvoton N9H30 BSP 外设驱动移植详解设备模型、设备名与 Kconfig 配置全解析 在 RT Thread 的 BSP 体操作系统嵌入式物联网嵌入式OSRTOSRT-Thread 平台下 SAM L10 同步 I2C 主机驱动HAL详解API、时序与时钟配置RT Thread 平台下 SAM L10 同步 I2C 主机驱动HAL详解API、时序与时钟配置 本篇技术指南以 RT Thread 仓库中 bsp/m操作系统嵌入式物联网嵌入式OSRTOS上一篇探索数据算法的瑰宝《Data Algorithms》开源项目指南下一篇【亲测免费】 推荐Android 图片裁剪库 —— Android Image Cropper创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考