苏州cms建站,网站基本参数设置模块,wordpress取消301跳转,服务器怎么做网站目录 前言CCF 介绍提供者和消费者的概念CCF 框架组成关系CCF 程序关键结构体 CCF 重要组成注册时钟未使用设备树的时钟注册操作使用设备树的时钟注册操作 从使用的角度看CCF 前言
linux 内核版本 v4.19 嵌入式平台rv1109 , 文中代码出处。
CCF 介绍
提供者和消费者的概念
C… 目录 前言CCF 介绍提供者和消费者的概念CCF 框架组成关系CCF 程序关键结构体 CCF 重要组成注册时钟未使用设备树的时钟注册操作使用设备树的时钟注册操作 从使用的角度看CCF 前言
linux 内核版本 v4.19 嵌入式平台rv1109 , 文中代码出处。
CCF 介绍
提供者和消费者的概念
CCF背后的主要思想是统一和抽象分布在不同SoC时钟驱动程序中的类似代码。这种标准化的方法引入了时钟提供者和时钟消费者的概念: 提供者是Linux内核驱动程序它连接到框架并提供对硬件的访问从而根据SoC数据表提供(使这些对消费者可用)时钟树(由于可以转储整个时钟树); 消费者是通过公共API访问框架的Linux内核驱动程序或子系统; 也就是说驱动程序既可以是提供者也可以是消费者(然后它可以使用它提供的一个或多个时钟也可以使用其他人提供的一个或多个时钟)。 CCF 框架组成关系
在使用CCF之前需要通过CONFIG_COMMON_CLK选项将其支持加入内核CCF 本身分为两部分 公共时钟框架核心:这是框架的核心当您添加新的驱动程序并提供struct clk的公共定义时不应该修改它它统一了框架级代码和传统的依赖于平台的实现这些实现过去常常在各种平台上复制。这一半还允许我们将消费者接口(也称为clk实现)包装在结构体clk_ops之上该结构体必须由每个时钟提供程序提供。 特定于硬件的那一半:它的目标是必须为每个新的硬件时钟写入的时钟设备。这需要驱动程序提供clk_ops结构体该结构体对应于用于对底层硬件进行操作的回调函数(这些回调函数由时钟的核心实现调用)以及包装和抽象时钟硬件的相应硬件特定结构。 这两部分通过struct clk_hw连接在一起。 CCF 程序关键结构体
struct clk_hw是CCF中每种时钟类型的基本结构。它可以看作是一个句柄用于从struct clk遍历到相应的特定于硬件的结构。
include/linux/clk-provider.h
struct clk_hw {struct clk_core *core;struct clk *clk; //时钟的消费者表示每个消费者API都依赖于这个结构体。const struct clk_init_data *init;
};clk 它由时钟框架分配和维护并在需要时提供给时钟使用者。每当消费者通过clk_get启动对CCF中的时钟设备(即clk_core)的访问时它都需要获得一个句柄即clk. init 在初始化底层时钟提供程序驱动程序的过程中调用clk_register()接口来注册时钟硬件。在此之前需要设置一些初始数据这些初始数据被抽象为struct clk_init_data数据结构。在初始化过程中clk_init_data中的数据用于初始化clk_core数据结构该数据结构对应于clk_hw。初始化完成后clk_init_data没有任何意义。
CCF 重要组成
注册时钟
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
int clk_hw_register(struct device *dev, struct clk_hw *hw)调用clk_hw_register()(它在内部调用__clk_core_init()来初始化时钟)时如果这个时钟有一个有效的父时钟它将在父时钟的子列表中结束。另一方面如果num_parent为0则将其放在clk_root_list中。否则它将挂起在clk_orphan_list中这意味着它没有有效的父节点。此外每当一个新的时钟被clk_init时CCF将遍历clk_orphan_list(孤儿时钟列表)并重新父化当前正在初始化的时钟的子时钟。这就是CCF保持时钟树与硬件拓扑一致的方式。另一方面struct clk是时钟设备的消费者端实例。 基本上所有用户对时钟设备的访问都会创建一个结构clk类型的访问句柄。当不同的用户访问相同的时钟设备时尽管在底层使用相同的struct clk_core实例但他们访问的句柄(struct clk)是不同的。 clk_hw_register 封装了clk_register只是为了兼容推荐使用clk_hw_register (不应该直接使用clk_reregister因为clk_reregister返回结构clk。这可能会导致混乱并打破提供者和使用者接口之间的严格分离)。
clk_hw_register / clk_register 的实现逻辑如下clk/clk.c 代码略 分配struct clk_core空间clk_hw-core 根据struct clk_hw指针提供的信息初始化clk的字段名称、ops、hw、flags、num_parents和parents_name。调用内核接口__clk_core_init来执行后续初始化操作包括构建时钟树层次结构。 通过内部内核接口clk_create_clk分配struct clk空间clk_hw-clk并返回此结构clk变量。 CCF框架负责建立整个抽象时钟树的树结构并维护其数据因此它通过drivers/clk/clk.c中定义的两个静态链表来实现这一点如下所示
static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);每当您在时钟hw上调用clk_hw_register()它在内部调用__clk_core_int来初始化时钟时如果该时钟有一个有效的父级它将最终出现在父级的子级列表中。另一方面若num_parent为0则将其放置在clk_root_list中。否则它将挂在clk_orpan_list中这意味着它没有有效的父级。此外每次新的clk为clk_init时CCF都会遍历clk_orpan_list孤立时钟的列表并为当前正在初始化的时钟的子级重新设置父级。这就是CCF保持时钟树与硬件拓扑一致的方式。 未使用设备树的时钟注册操作
由于知道clk_register的目的只是注册到公共时钟框架因此消费者无法知道如何定位clk。因此对于底层时钟提供程序驱动程序除了调用clk_register函数以注册到公共时钟框架之外还必须在clk_register之后立即调用clk_register_clkdev以便用名称绑定时钟否则时钟使用者将不知道如何定位时钟。因此内核使用struct clk_lookup顾名思义来查找可用的时钟。为了使用基于hw的API强制实现提供者和使用者代码之间的分离代码中的clk_hw_register_clkdev和clk_register_clkdev。
clk_lookup 结构
struct clk_lookup {struct list_head node;const char *dev_id;const char *con_id;struct clk *clk;struct clk_hw *clk_hw;
};dev_id和con_id用于识别/查找适当的clk。这个clk是相应的底层时钟。node是挂在全局时钟列表中 clk_hw_register_clkdev -- _clkdev_add
static void __clkdev_add(struct clk_lookup *cl)
{mutex_lock(clocks_mutex);list_add_tail(cl-node, clocks);mutex_unlock(clocks_mutex);
}void clkdev_add(struct clk_lookup *cl)
{if (!cl-clk_hw)cl-clk_hw __clk_get_hw(cl-clk);__clkdev_add(cl);
}
EXPORT_SYMBOL(clkdev_add);使用设备树的时钟注册操作
… 未完
从使用的角度看CCF