【linux kernel】一文总结linux的clk框架

一、clk框架简介

linux内核中实现了一个CLK子系统,用于对上层提供各模块(例如需要时钟信号的外设,USB等)的时钟驱动接口,对下层提供具体SOC的时钟操作细节:

一般情况下,在可运行linux的处理器平台中,都存在非常复杂的时钟树(clock tree)关系,也一定会有一个非常庞大和复杂的树状图,用于描述与时钟相关的器件,以及这些器件输出的clock关系。查看手册都会存在类似下列的内容:

一款处理器中与时钟相关的器件主要包括有:

  • 用于产生 CLOCK 的 Oscillator(有源振荡器,也称作谐振荡器)或者Crystal(无源振荡器,也称晶振)。
  • 用于倍频的 PLL(锁相环,Phase Locked Loop)。
  • 用于分频的Divider。
  • 用于多路选择的 MUX。
  • 用于CLOCK ENABLE控制的与门。
  • 使用 CLOCK 的硬件模块(也可称为CONSUMER)。

linux内核中与clk框架相关源文件如下:

/include/linux/clk.h
/include/linux/clkdev.h
/include/linux/clk-provider.h
/include/linux/clk-conf.h
------------------------------------------------------
/drivers/clk/clk-devres.c
/drivers/clk-bulk.c
/drivers/clkdev.c
/drivers/clk.c
/drivers/clk-divider.c
/drivers/clk-fixed-factor.c
/drivers/clk-fixed-rate.c
/drivers/clk-gate.c
/drivers/clk-multiplier.c
/drivers/clk-mux.c
/drivers/clk-composite.c
/drivers/clk-fractional-divider.c
/drivers/clk-gpio.c
/drivers/clk-conf.c

二、clk框架接口

1、基于linux时钟子系统对接底层时钟操作的API

linux时钟子系统对接底层,也就是具体硬件常用的API可视为clk provider常用的接口函数,定义在linux/include/linux/clk-provider.h文件中。不同版本linux内核中对于clk-probider.h实现的而接口存在出入,参见源码更进一步获取接口和使用方法。

  • 注册/注销时钟
//注册一个新的时钟。通常在设备驱动程序中使用,以向时钟框架添加新的时钟源。
struct clk *clk_register(struct device *dev, struct clk_hw *hw);

//带资源管理注册时钟
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw);

//卸载时钟
void clk_unregister(struct clk *clk);
//带资源管理卸载时钟
void devm_clk_unregister(struct device *dev, struct clk *clk);

2、驱动中常使用的API

芯片厂家会根据clk框架,对下层(即底层硬件)设计出接口,以供上层驱动接口调用,在内核中,提供的接口主要由/include/linux/clk.h文件导出,使用这些API接口时,需包含linux/clk.h头文件:

#include <linux/clk.h>
  • 获取struct clk指针:
struct clk *devm_clk_get(struct device *dev, const char *id)(推荐使用,可以自动释放)
struct clk *clk_get(struct device *dev, const char *id)
static inline struct clk *devm_clk_get_optional(struct device *dev, constchar *id)

//(推荐使用,整组获取,整组开关)
static inline int __must_check devm_clk_bulk_get(struct device *dev, intnum_clks, struct clk_bulk_data *clks)
static inline int __must_check devm_clk_bulk_get_optional(struct device *dev,int num_clks, struct clk_bulk_data *clks)
static inline int __must_check devm_clk_bulk_get_all(struct device *dev,struct clk_bulk_data **clks)
  • 获取/设置时钟频率
//根据给定的目标频率计算最接近的可实现频率。这个函数通常在设置时钟频率之前调用,以确保设置的频率是硬件支持的频率之一。
long clk_round_rate(struct clk *clk, unsigned long rate)

//获取时钟频率
unsigned long clk_get_rate(struct clk *clk)

//设置时钟频率
int clk_set_rate(struct clk *clk, unsigned long rate)
  • 准备/使能clk:
/* 开时钟前调用,可能会造成休眠,所以把休眠部分放到这里,可以原子操作的放到enable里 */
int clk_prepare(struct clk *clk)

/* 停止clock后的善后工作,可能会睡眠。*/
void clk_unprepare(struct clk *clk)

/* 原子操作,打开时钟,这个函数必须在产生实际可用的时钟信号后才能返回,不会睡眠 */
int clk_enable(struct clk *clk)

/* 原子操作,关闭时钟,不会睡眠 */
void clk_disable(struct clk *clk)

上述两套API的本质,是把CLOCK的启动/停止分为Atomic和Non-atomic两个阶段,以方便实现和调用。因此上面所说的“不会睡眠/可能会睡眠”,有两个角度的含义:

  • 一是告诉底层的CLOCK Driver,需把可能引起睡眠的操作,放到Prepare()/Unprepare()中实现,一定不能放到Enable()/Disable()中;
  • 二是提醒上层使用CLOCK的Driver,调用Prepare/Unprepare 接口时可能会睡眠,千万不能在Atomic上下文(例如内部包含Mutex 锁、中断关闭、Spinlock 锁保护的区域)调用,而调用Enable()/Disable()接口则可放心。

另外,CLOCK的Enable()/Disable()为什么需要睡眠呢?例如Enable PLL CLOCK,在启动PLL后,需要等待它稳定,然而PLL的稳定时间是很长的,因此这段时间要需要把CPU让出(进程睡眠),不然就会浪费CPU了。

最后,为什么会实现合在一起的clk_prepare_enable()/clk_disable_unprepare()接口呢?如果调用者能确保是在Non-atomic上下文中调用,就可以顺序调用prepare()/enable()、disable()/unprepared(),为了方便Colck框架就封装了这两个接口。

备注:使用clk_prepare_enable / clk_disable_unprepareclk_prepare_enable / clk_disable_unprepare(或者
clk_enable / clk_disable) 必须成对,以使引用计数正确。

三、CLK核心的数据结构和API

1、struct clk_notifier

stuct clk_notifier用于将CLK与通知器进行关联,也就是定义clk的通知器,基于srcu实现。该结构实现如下(/linux/include/linux/clk.h):

struct clk_notifier {
   
	struct clk			*clk;  //与该通知器关联的clk。
	struct srcu_notifier_head	notifier_head; //用于这个CLK的blocking_notifier_head通知器。
	struct list_head		node;
};

常用API:

//注册一个通知块(notifier block),以便在指定时钟发生事件(例如频率变化)时接收通知。
int clk_notifier_register(struct clk *clk, struct notifier_block *nb);

//注销一个通知块
int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);

//带资源管理

相关推荐

  1. 总结vue和react区别

    2024-07-18 21:58:04       28 阅读
  2. 【Android】总结Androidinit语言

    2024-07-18 21:58:04       21 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 21:58:04       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 21:58:04       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 21:58:04       57 阅读
  4. Python语言-面向对象

    2024-07-18 21:58:04       68 阅读

热门阅读

  1. 【ZMH的学习笔记】修饰符类型

    2024-07-18 21:58:04       19 阅读
  2. .Net C# Using 关键字的介绍与使用

    2024-07-18 21:58:04       21 阅读
  3. 前端实现将多个页面导出为pdf(分页)

    2024-07-18 21:58:04       18 阅读
  4. .NET_依赖注入_相关概念及基础使用

    2024-07-18 21:58:04       22 阅读
  5. ES6模块化方案导入导出模块方法

    2024-07-18 21:58:04       21 阅读
  6. 设备树节点和struct device的关系及示例

    2024-07-18 21:58:04       18 阅读
  7. Html_Css问答集(8)

    2024-07-18 21:58:04       18 阅读
  8. APP开发者选择苹果企业签名的理由是什么?

    2024-07-18 21:58:04       21 阅读
  9. 负载均衡轮询逻辑

    2024-07-18 21:58:04       19 阅读
  10. swift小知识点(二)

    2024-07-18 21:58:04       18 阅读
  11. Redis常见阻塞原因

    2024-07-18 21:58:04       22 阅读
  12. Pandas库学习之DataFrame.replace()函数

    2024-07-18 21:58:04       21 阅读