部分内容来自下面几位博主的文章,如有侵权,联系我删除。
http://www.wowotech.net/pm_subsystem/clk_overview.html
https://blog.csdn.net/cc289123557/article/details/80098586
时钟管理模块是linux系统为统一管理各硬件的时钟而实现管理框架,负责所有模块的时钟调节和电源管理。时钟管理模块主要负责处理各硬件模块的工作频率调节及电源切换管理。一个硬件模块要正常工作,必须先配置好硬件的工作频率、打开电源开关、总线访问开关等操作,时钟管理模块为设备驱动提供统一的操作接口,使驱动不用关心时钟硬件实现的具体细节。
common clock framework是用来管理系统clock资源的子系统,根据职能,可分为三个部分:
1)向其它driver提供操作clocks的通用API。
2)实现clock控制的通用逻辑,这部分和硬件无关。
3)将和硬件相关的clock控制逻辑封装成操作函数集,交由底层的platform开发者实现,由通用逻辑调用。
如今,可运行Linux的主流处理器平台,都有非常复杂的clock tree,我们随便拿一个处理器的spec,查看clock相关的章节,一定会有一个非常庞大和复杂的树状图,这个图由clock相关的器件,以及这些器件输出的clock组成。下图是一个示例:
clock相关的器件包括:用于产生clock的oscillator(有源振荡器,也称作谐振荡器)或者Crystal(无源振荡器,也称晶振);用于倍频的PLL(锁相环,Phase Locked Loop);用于分频的divider;用于多路选择的Mux;用于clock enable控制的与门;使用clock的硬件模块(可称作consumer);等等。
common clock framework的管理对象,就是上图蓝色字体描述的clock(在软件中用struct clk抽象,以后就简称clk),主要内容包括(不需要所有clk都支持):
1)enable/disable clk。
2)设置clk的频率。
3)选择clk的parent,例如hw3_clk可以选择osc_clk、pll2_clk或者pll3_clk作为输入源。
时钟的基本种类
固定速率 |
比如晶振 |
门时钟 | 和上级时钟同频,只能打开和关闭操作 |
MUX |
多选一(也叫多路复用) |
固定倍频 |
上级时钟的频率有固定倍频或者分频,不能关闭 |
分频 |
上级时钟的频率分频,可以选择不同的分频比 |
struct clk {
const char *name; //名字用来在全局链表里查找clk用的
const struct clk_ops *ops; //抽象的时钟ops操作,由具体芯片实现
struct clk_hw *hw; //clk_hw后面有专门介绍
struct module *owner;
struct clk *parent; //父时钟
const char **parent_names; //父时钟的名字字符串数组
struct clk **parents;
u8 num_parents; //父时钟的个数
u8 new_parent_index;
unsigned long rate; //频率
unsigned long new_rate;
struct clk *new_parent;
struct clk *new_child;
unsigned long flags;
unsigned int enable_count;
unsigned int prepare_count;
unsigned long accuracy;
struct hlist_head children; //链接自己下面从属的子时钟
struct hlist_node child_node; //本身被当作一个节点链入它的父链表
unsigned int notifier_count;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
#endif
struct kref ref;
};
一个系统的clock tree是固定的,因此clock的数目和用途也是固定的。假设上面图片所描述的是一个系统,它的clock包括osc_clk、pll1_clk、pll2_clk、pll3_clk、hw1_clk、hw2_clk和hw3_clk。我们完全可以通过名字,抽象这7个clock,进行开/关、rate调整等操作。但这样做有一个缺点:不能很好的处理clock之间的级联关系,如hw2_clk和hw3_clk都关闭后,pll2_clk才能关闭。因此就引入struct clk结构,以链表的形式维护这种关系。
同样的道理,系统的struct clk,也是固定的,由clock driver在系统启动时初始化完毕,需要访问某个clock时,只要获取它对应的struct clk结构即可。怎么获取呢?可以通过名字索引啊!很长一段时间内,kernel及driver就是使用这种方式管理和使用clock的。
最后,设备(由struct device表示)对应的clock(由struct clk表示)也是固定的啊,可不可以找到设备就能找到clock?可以,不过需要借助device tree。
注:对使用者(device driver)来说,struct clk只是访问clock的一个句柄,不用关心它内部的具体形态。
管理clock的最终目的,是让device driver可以方便的使用,这些是通过include/linux/clk.h中的通用API体现的,当然下面这些只是提供给写设备驱动的人,调用的通用接口,如下
struct clk *clk_get(struct device *dev, const char *con_id);
void clk_put(struct clk *clk);
struct clk *devm_clk_get(struct device *dev, const char *id);;
void devm_clk_put(struct device *dev, struct clk *clk);
struct clk *clk_get_sys(const char *dev_id, const char *con_id);
struct clk *of_clk_get(struct device_node *np, int index);
struct clk *of_clk_get_by_name(struct device_node *np, const char *name);
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec);
a)clk_get,以device指针或者id字符串(可以看作name)为参数,查找clock。
a1)dev和id的任意一个可以为空。如果id为空,则必须有device tree的支持才能获得device对应的clk;
a2)根据具体的平台实现,id可以是一个简单的名称,也可以 是一个预先定义的、唯一的标识(一般在平台提供的头文件中定义,如mach/clk.h);
a3)不可以在中断上下文调用。b)devm_clk_get,和clk_get一样,只是使用了device resource management,可以自动释放。
c)clk_put、devm_clk_put,get的反向操作,一般和对应的get API成对调用。
d)clk_get_sys,类似clk_get,不过使用device的name替代device结构。
e)of_clk_get、of_clk_get_by_name、of_clk_get_from_provider,device tree相关的接口,直接从相应的DTS node中,以index、name等为索引,获取clk,后面会详细说明。
struct clk *clk_get(struct device *dev, const char *con_id)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
struct clk *clk;
if (dev) {
clk = of_clk_get_by_name(dev->of_node, con_id); /* 从设备树节点得到clk */
if (!IS_ERR(clk)) /* 注意这里对错误判断取反了,即没错的话会直接返回clk */
return clk;
if (PTR_ERR(clk) == -EPROBE_DEFER) /* 重新获取 */
return clk;
}
return clk_get_sys(dev_id, con_id); /* 设备树节点没找到,从系统注册链表中搜索得到 */
}
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
struct clk_lookup *cl;
mutex_lock(&clocks_mutex);
cl = clk_find(dev_id, con_id); /* 根据设备名或链接名在clock链表中查找到对应的cl*/
if (cl && !__clk_get(cl->clk)) /* 这里对找到的时钟的引用计数+1 */
cl = NULL;
mutex_unlock(&clocks_mutex);
return cl ? cl->clk : ERR_PTR(-ENOENT);
}
/*
* Find the correct struct clk for the device and connection ID.
* We do slightly fuzzy matching here:
* An entry with a NULL ID is assumed to be a wildcard.
* If an entry has a device ID, it must match
* If an entry has a connection ID, it must match
* Then we take the most specific entry - with the following
* order of precedence: dev+con > dev only > con only. 这是重点,即匹配优先级
*/
static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
{
struct clk_lookup *p, *cl = NULL;
int match, best_found = 0, best_possible = 0;
if (dev_id) /* 设备名称 */
best_possible += 2;
if (con_id) /* 连接名称(可以是pclk,uart_clk,phy,pci,总是一般都是总线的时钟名称) */
best_possible += 1;
list_for_each_entry(p, &clocks, node) { /* clocks链表中查找,根据dev+con > dev only > con优先级来匹配 */
match = 0;
if (p->dev_id) {
if (!dev_id || strcmp(p->dev_id, dev_id))
continue;
match += 2;
}
if (p->con_id) {
if (!con_id || strcmp(p->con_id, con_id))
continue;
match += 1;
}
if (match > best_found) {
cl = p;
if (match != best_possible)
best_found = match;
else
break;
}
}
return cl;
}
struct clk *devm_clk_get(struct device *dev, const char *id)
{
struct clk **ptr, *clk;
/* 设备资源自动管理,卸载设备时会调用devm_clk_release,做清理工作 */
ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
clk = clk_get(dev, id); /* 这里的核心和就是clk_get */
if (!IS_ERR(clk)) {
*ptr = clk;
devres_add(dev, ptr); /* 资源绑定到这设备资源链表上 */
} else {
devres_free(ptr);
}
return clk;
}
void devm_clk_put(struct device *dev, struct clk *clk)
{
int ret;
ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
WARN_ON(ret);
}
void clk_put(struct clk *clk)
{
__clk_put(clk);
}
void clk_put(struct clk *clk)
{
__clk_put(clk);
}
void __clk_put(struct clk *clk)
{
struct module *owner;
if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
return;
clk_prepare_lock();
owner = clk->owner;
kref_put(&clk->ref, __clk_release); /* 减小使用计数,减小为0时,则调用__clk_release函数 */
clk_prepare_unlock();
module_put(owner);
}
/*
* Free memory allocated for a clock.
* Caller must hold prepare_lock.
*/
static void __clk_release(struct kref *ref)
{
struct clk *clk = container_of(ref, struct clk, ref);
int i = clk->num_parents;
kfree(clk->parents);
while (--i >= 0) /* 一个clk有时可以有多个父节点可选,则里如果这clk释放,则为本节点保存的所有的父节点的信息也要释放 */
kfree(clk->parent_names[i]);
kfree(clk->parent_names); /* 释放存放父节点的数组 */
kfree(clk->name); /* 释放本节点名字 */
kfree(clk); /* 释放本clk */
}
设备树的几个因为我还没学设备树,待学完设备树再在这里添加
int clk_prepare(struct clk *clk);
void clk_unprepare(struct clk *clk);
int clk_enable(struct clk *clk);
void clk_disable(struct clk *clk);
unsigned long clk_get_rate(struct clk *clk);
int clk_set_rate(struct clk *clk, unsigned long rate);
long clk_round_rate(struct clk *clk, unsigned long rate);
struct clk *clk_get_parent(struct clk *clk);
int clk_set_parent(struct clk *clk, struct clk *parent);
static inline int clk_prepare_enable(struct clk *clk);
static inline void clk_disable_unprepare(struct clk *clk);
a)clk_enable/clk_disable,启动/停止clock。不会睡眠。
b)clk_prepare/clk_unprepare,启动clock前的准备工作/停止clock后的善后工作。可能会睡眠。
c)clk_get_rate/clk_set_rate/clk_round_rate,clock频率的获取和设置,其中clk_set_rate可能会不成功(例如没有对应的分频比),此时会返回错误。如果要确保设置成功,则需要先调用clk_round_rate接口,得到和需要设置的rate比较接近的那个值。
d)获取/选择clock的parent clock。
e)clk_prepare_enable,将clk_prepare和clk_enable组合起来,一起调用。clk_disable_unprepare,将clk_disable和clk_unprepare组合起来,一起调用。
注:prepare/unprepare,enable/disable的说明。
这两套API的本质,是把clock的启动/停止分为atomic和non-atomic两个阶段,以方便实现和调用。因此上面所说的“不会睡眠/可能会睡眠”,有两个角度的含义:一是告诉底层的clock driver,请把可能引起睡眠的操作,放到prepare/unprepare中实现,一定不能放到enable/disable中;二是提醒上层使用clock的driver,调用prepare/unprepare接口时可能会睡眠哦,千万不能在atomic上下文(例如中断处理中)调用哦,而调用enable/disable接口则可放心。
另外,clock的开关为什么需要睡眠呢?这里举个例子,例如enable PLL clk,在启动PLL后,需要等待它稳定。而PLL的稳定时间是很长的,这段时间要把CPU交出(进程睡眠),不然就会浪费CPU。
最后,为什么会有合在一起的clk_prepare_enable/clk_disable_unprepare接口呢?如果调用者能确保是在non-atomic上下文中调用,就可以顺序调用prepare/enable、disable/unprepared,为了简单,framework就帮忙封装了这两个接口。
/**
* clk_enable - ungate a clock
* @clk: the clk being ungated
*
* clk_enable must not sleep, which differentiates it from clk_prepare. In a
* simple case, clk_enable can be used instead of clk_prepare to ungate a clk
* if the operation will never sleep. One example is a SoC-internal clk which
* is controlled via simple register writes. In the complex case a clk ungate
* operation may require a fast and a slow part. It is this reason that
* clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare
* must be called before clk_enable. Returns 0 on success, -EERROR
* otherwise.
*/
int clk_enable(struct clk *clk)
{
unsigned long flags;
int ret;
flags = clk_enable_lock(); /* 时钟操作是不能并发的,所以这里要上锁 */
ret = __clk_enable(clk); /* 使能时钟 */
clk_enable_unlock(flags);
return ret;
}
static int __clk_enable(struct clk *clk)
{
int ret = 0;
if (!clk)
return 0;
if (WARN_ON(clk->prepare_count == 0)) /* 在enable前必须先执行clk_prepare */
return -ESHUTDOWN;
if (clk->enable_count == 0) { /* 如果没使能,则调用函数,如果使能了,则把使能次数++标记有多少设备在用设个时钟 */
ret = __clk_enable(clk->parent); /* 使能某个时钟前,它的父时钟必须先使能(这里是递归的检查调用和检查父时钟) */
if (ret)
return ret;
if (clk->ops->enable) {
ret = clk->ops->enable(clk->hw); /* 具体的某个时钟的使能,是由具体的clk里面的ops的enable在执行的,即在注册这个clk时注册的 */
if (ret) {
__clk_disable(clk->parent);
return ret;
}
}
}
clk->enable_count++;
return 0;
}
/**
* clk_disable - gate a clock
* @clk: the clk being gated
*
* clk_disable must not sleep, which differentiates it from clk_unprepare. In
* a simple case, clk_disable can be used instead of clk_unprepare to gate a
* clk if the operation is fast and will never sleep. One example is a
* SoC-internal clk which is controlled via simple register writes. In the
* complex case a clk gate operation may require a fast and a slow part. It is
* this reason that clk_unprepare and clk_disable are not mutually exclusive.
* In fact clk_disable must be called before clk_unprepare.
*/
void clk_disable(struct clk *clk)
{
unsigned long flags;
if (IS_ERR_OR_NULL(clk))
return;
flags = clk_enable_lock(); /* 保证时钟使能和关闭是不能并发的,不然一个cpu开,一个cpu关,就出问题了 */
__clk_disable(clk); /* 关时钟 */
clk_enable_unlock(flags);
}
static void __clk_disable(struct clk *clk)
{
if (!clk)
return;
if (WARN_ON(clk->enable_count == 0)) /* 0代表以及关了 */
return;
/* 每个设备使用关联着时钟,都需要使能,每个设备注销也都会关掉这个时钟,
> 0表示还有设备在yong,这里把使用的设备减少1个就好 */
if (--clk->enable_count > 0)
return;
if (clk->ops->disable)
clk->ops->disable(clk->hw); /* 关这个时钟 */
/* 依次的关父设备的时钟,当然只有在父设备只由这一个子设备的时候才能关掉,否则还是和上面一样,只是时钟使用者减小1 */
__clk_disable(clk->parent);
}
/**
* clk_prepare - prepare a clock source
* @clk: the clk being prepared
*
* clk_prepare may sleep, which differentiates it from clk_enable. In a simple
* case, clk_prepare can be used instead of clk_enable to ungate a clk if the
* operation may sleep. One example is a clk which is accessed over I2c. In
* the complex case a clk ungate operation may require a fast and a slow part.
* It is this reason that clk_prepare and clk_enable are not mutually
* exclusive. In fact clk_prepare must be called before clk_enable.
* Returns 0 on success, -EERROR otherwise.
*/
int clk_prepare(struct clk *clk)
{
int ret;
clk_prepare_lock(); /* 防止并发 */
ret = __clk_prepare(clk); /* 这里就和enabel一样了 */
clk_prepare_unlock();
return ret;
}
int __clk_prepare(struct clk *clk)
{
int ret = 0;
if (!clk)
return 0;
/* 只由在clk的prepare_count为0时才使能,其它时候表示已经使能过了,后面只是计数增加 */
if (clk->prepare_count == 0) {
ret = __clk_prepare(clk->parent); /* 父clk的prepare使能的情况下才,使能子clk */
if (ret)
return ret;
if (clk->ops->prepare) {
ret = clk->ops->prepare(clk->hw); /* 使能clk硬件 */
if (ret) {
__clk_unprepare(clk->parent);
return ret;
}
}
}
clk->prepare_count++;
return 0;
}
/**
* clk_unprepare - undo preparation of a clock source
* @clk: the clk being unprepared
*
* clk_unprepare may sleep, which differentiates it from clk_disable. In a
* simple case, clk_unprepare can be used instead of clk_disable to gate a clk
* if the operation may sleep. One example is a clk which is accessed over
* I2c. In the complex case a clk gate operation may require a fast and a slow
* part. It is this reason that clk_unprepare and clk_disable are not mutually
* exclusive. In fact clk_disable must be called before clk_unprepare.
*/
void clk_unprepare(struct clk *clk)
{
if (IS_ERR_OR_NULL(clk))
return;
clk_prepare_lock(); /* 锁保护,防止并发 */
__clk_unprepare(clk); /* 退出保护 */
clk_prepare_unlock();
}
void __clk_unprepare(struct clk *clk)
{
if (!clk)
return;
if (WARN_ON(clk->prepare_count == 0)) /* 0表示时钟已经关闭了 */
return;
if (--clk->prepare_count > 0) /* 还有别的设备在用,暂时不能关 */
return;
WARN_ON(clk->enable_count > 0);
if (clk->ops->unprepare)
clk->ops->unprepare(clk->hw); /* 关时钟 */
__clk_unprepare(clk->parent); /* 父时钟的引用次数也减少1次 */
}
/**
* clk_get_rate - return the rate of clk
* @clk: the clk whose rate is being returned
*
* Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag
* is set, which means a recalc_rate will be issued.
* If clk is NULL then returns 0.
*/
unsigned long clk_get_rate(struct clk *clk)
{
unsigned long rate;
clk_prepare_lock();
/* 这个时钟频率如果标记了使用时每次都计算的话则调用时钟计算函数 */
if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))
__clk_recalc_rates(clk, 0);
rate = __clk_get_rate(clk); /* 得到时钟频率 */
clk_prepare_unlock();
return rate;
}
unsigned long __clk_get_rate(struct clk *clk)
{
unsigned long ret;
if (!clk) { /* 时钟必须存在 */
ret = 0;
goto out;
}
ret = clk->rate; /* 返回时钟频率 */
if (clk->flags & CLK_IS_ROOT) /* 根时钟就不检查下面的父时钟,其它时钟都要检查父时钟存在 */
goto out;
if (!clk->parent)
ret = 0;
out:
return ret;
}
/**
* __clk_recalc_rates
* @clk: first clk in the subtree
* @msg: notification type (see include/linux/clk.h)
*
* Walks the subtree of clks starting with clk and recalculates rates as it
* goes. Note that if a clk does not implement the .recalc_rate callback then
* it is assumed that the clock will take on the rate of its parent.
*
* clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
* if necessary.
*
* Caller must hold prepare_lock.
*/
static void __clk_recalc_rates(struct clk *clk, unsigned long msg)
{
unsigned long old_rate;
unsigned long parent_rate = 0;
struct clk *child;
old_rate = clk->rate; /* 保存老的时钟 */
if (clk->parent) /* 得到父时钟频率,这里要说的是,没父时钟,则速率就是上面初始化的默认值0了 */
parent_rate = clk->parent->rate;
/* 根据clk的父时钟,计算clk的新时钟速率 */
clk->rate = clk_recalc(clk, parent_rate);
/*
* ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE
* & ABORT_RATE_CHANGE notifiers
*/
if (clk->notifier_count && msg)
__clk_notify(clk, msg, old_rate, clk->rate); /* 时钟更新需要通知使用它的子时钟 */
/* clk的时钟更新了,clk的子时钟们也是要重新更新计算时钟速率的 */
hlist_for_each_entry(child, &clk->children, child_node)
__clk_recalc_rates(child, msg);
}
static unsigned long clk_recalc(struct clk *clk, unsigned long parent_rate)
{
if (clk->ops->recalc_rate)
return clk->ops->recalc_rate(clk->hw, parent_rate); //根据父时钟重新计算时钟
return parent_rate;
}
/**
* clk_set_rate - specify a new rate for clk
* @clk: the clk whose rate is being changed
* @rate: the new rate for clk
*
* In the simplest case clk_set_rate will only adjust the rate of clk.
*
* Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to
* propagate up to clk's parent; whether or not this happens depends on the
* outcome of clk's .round_rate implementation. If *parent_rate is unchanged
* after calling .round_rate then upstream parent propagation is ignored. If
* *parent_rate comes back with a new rate for clk's parent then we propagate
* up to clk's parent and set its rate. Upward propagation will continue
* until either a clk does not support the CLK_SET_RATE_PARENT flag or
* .round_rate stops requesting changes to clk's parent_rate.
*
* Rate changes are accomplished via tree traversal that also recalculates the
* rates for the clocks and fires off POST_RATE_CHANGE notifiers.
*
* Returns 0 on success, -EERROR otherwise.
*/
int clk_set_rate(struct clk *clk, unsigned long rate)
{
struct clk *top, *fail_clk;
int ret = 0;
if (!clk)
return 0;
/* prevent racing with updates to the clock topology */
clk_prepare_lock();
/* bail early if nothing to do */
if (rate == clk_get_rate(clk)) /* 新设置的频率和之前的频率比较一下,一样的话就不设置了 */
goto out;
if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) {
ret = -EBUSY;
goto out;
}
/* calculate new rates and get the topmost changed clock */
top = clk_calc_new_rates(clk, rate); /* 计算和设置值接近的新频率 */
if (!top) {
ret = -EINVAL;
goto out;
}
/* notify that we are about to change rates */
fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
if (fail_clk) {
pr_debug("%s: failed to set %s rate\n", __func__,
fail_clk->name);
clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
ret = -EBUSY;
goto out;
}
/* change the rates */
clk_change_rate(top);
out:
clk_prepare_unlock();
return ret;
}
/*
* calculate the new rates returning the topmost clock that has to be
* changed.
*/
static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
{
struct clk *top = clk;
struct clk *old_parent, *parent;
unsigned long best_parent_rate = 0;
unsigned long new_rate;
int p_index = 0;
/* sanity */
if (IS_ERR_OR_NULL(clk))
return NULL;
/* save parent rate, if it exists */
parent = old_parent = clk->parent; /* 由父时钟开始计算 */
if (parent)
best_parent_rate = parent->rate;
/* find the closest rate and parent clk/rate */
if (clk->ops->determine_rate) {
new_rate = clk->ops->determine_rate(clk->hw, rate,
&best_parent_rate,
&parent); /* 计算一个要求clk的新速率(这里可能更换父时钟) */
} else if (clk->ops->round_rate) {
new_rate = clk->ops->round_rate(clk->hw, rate,
&best_parent_rate); /* 由父时钟计算一个接近要求的新速率 */
} else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) {
/* pass-through clock without adjustable parent */
clk->new_rate = clk->rate; /* 没父时钟的情况 */
return NULL;
} else {
/* pass-through clock with adjustable parent */
top = clk_calc_new_rates(parent, rate); /* 递归的计算父时钟速率 */
new_rate = parent->new_rate;
goto out;
}
/* some clocks must be gated to change parent */
if (parent != old_parent &&
(clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
pr_debug("%s: %s not gated but wants to reparent\n",
__func__, clk->name);
return NULL;
}
/* try finding the new parent index 前面如果更换了父时钟,则这里尝试找到新的父索引 */
if (parent) {
p_index = clk_fetch_parent_index(clk, parent);
if (p_index < 0) {
pr_debug("%s: clk %s can not be parent of clk %s\n",
__func__, parent->name, clk->name);
return NULL;
}
}
/* 父时钟频率如果和之前的不一样,则要重新计算本时钟频率 */
if ((clk->flags & CLK_SET_RATE_PARENT) && parent &&
best_parent_rate != parent->rate)
top = clk_calc_new_rates(parent, best_parent_rate); /* 递归计算 */
out:
clk_calc_subtree(clk, new_rate, parent, p_index); /* 最后新设置的时钟已经改变,他的子时钟也都要一次更新 */
return top;
}
static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
struct clk *new_parent, u8 p_index)
{
struct clk *child;
clk->new_rate = new_rate;
clk->new_parent = new_parent;
clk->new_parent_index = p_index; /* 新父时钟是那个索引 */
/* include clk in new parent's PRE_RATE_CHANGE notifications */
clk->new_child = NULL;
if (new_parent && new_parent != clk->parent) /* 新的父时钟为NULL,则不更换父时钟,只更新频率 */
new_parent->new_child = clk;
hlist_for_each_entry(child, &clk->children, child_node) {
child->new_rate = clk_recalc(child, new_rate); /* 重新计算子时钟的里面所有时钟 */
clk_calc_subtree(child, child->new_rate, NULL, 0); /* 递归的跟新子时钟的所有时钟 */
}
}
/**
* clk_round_rate - round the given rate for a clk
* @clk: the clk for which we are rounding a rate
* @rate: the rate which is to be rounded
*
* Takes in a rate as input and rounds it to a rate that the clk can actually
* use which is then returned. If clk doesn't support round_rate operation
* then the parent rate is returned.
*/
long clk_round_rate(struct clk *clk, unsigned long rate)
{
unsigned long ret;
clk_prepare_lock();
ret = __clk_round_rate(clk, rate);
clk_prepare_unlock();
return ret;
}
/**
* __clk_round_rate - round the given rate for a clk
* @clk: round the rate of this clock
* @rate: the rate which is to be rounded
*
* Caller must hold prepare_lock. Useful for clk_ops such as .set_rate
*/
unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
{
unsigned long parent_rate = 0;
struct clk *parent;
if (!clk)
return 0;
parent = clk->parent;
if (parent)
parent_rate = parent->rate;
if (clk->ops->determine_rate)
return clk->ops->determine_rate(clk->hw, rate, &parent_rate,
&parent); /* 精确计算时钟更新 */
else if (clk->ops->round_rate)
return clk->ops->round_rate(clk->hw, rate, &parent_rate); /* 计算最接近的时钟 */
else if (clk->flags & CLK_SET_RATE_PARENT)
return __clk_round_rate(clk->parent, rate); /* 递归的把这个时钟频率传递给父时钟 */
else
return clk->rate;
}
/**
* clk_get_parent - return the parent of a clk
* @clk: the clk whose parent gets returned
*
* Simply returns clk->parent. Returns NULL if clk is NULL.
*/
struct clk *clk_get_parent(struct clk *clk)
{
struct clk *parent;
clk_prepare_lock();
parent = __clk_get_parent(clk);
clk_prepare_unlock();
return parent;
}
struct clk *__clk_get_parent(struct clk *clk)
{
return !clk ? NULL : clk->parent;
}
/**
* clk_set_parent - switch the parent of a mux clk
* @clk: the mux clk whose input we are switching
* @parent: the new input to clk
*
* Re-parent clk to use parent as its new input source. If clk is in
* prepared state, the clk will get enabled for the duration of this call. If
* that's not acceptable for a specific clk (Eg: the consumer can't handle
* that, the reparenting is glitchy in hardware, etc), use the
* CLK_SET_PARENT_GATE flag to allow reparenting only when clk is unprepared.
*
* After successfully changing clk's parent clk_set_parent will update the
* clk topology, sysfs topology and propagate rate recalculation via
* __clk_recalc_rates.
*
* Returns 0 on success, -EERROR otherwise.
*/
//切换多路复用时钟
int clk_set_parent(struct clk *clk, struct clk *parent)
{
int ret = 0;
int p_index = 0;
unsigned long p_rate = 0;
if (!clk)
return 0;
/* verify ops for for multi-parent clks */
if ((clk->num_parents > 1) && (!clk->ops->set_parent)) /* 父时钟大于1,且有可设置的接口时才可以更换 */
return -ENOSYS;
/* prevent racing with updates to the clock topology */
clk_prepare_lock();
if (clk->parent == parent) /* 新设置的父时钟和老的一样,则不用更新 */
goto out;
/* check that we are allowed to re-parent if the clock is in use */
if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
ret = -EBUSY;
goto out;
}
/* try finding the new parent index */
if (parent) {
p_index = clk_fetch_parent_index(clk, parent); /* 新设置的父时钟在clk的所有父时钟表的位置 */
p_rate = parent->rate;
if (p_index < 0) {
pr_debug("%s: clk %s can not be parent of clk %s\n",
__func__, parent->name, clk->name);
ret = p_index;
goto out;
}
}
/* propagate PRE_RATE_CHANGE notifications */
ret = __clk_speculate_rates(clk, p_rate); /* 通知频率更新 */
/* abort if a driver objects */
if (ret & NOTIFY_STOP_MASK)
goto out;
/* do the re-parent */
ret = __clk_set_parent(clk, parent, p_index); /* 设置clk的父时钟 */
/* propagate rate an accuracy recalculation accordingly /* 父时钟更新了,子时钟的频率可能改变,也要跟新和通知 */ */
if (ret) {
__clk_recalc_rates(clk, ABORT_RATE_CHANGE);
} else {
__clk_recalc_rates(clk, POST_RATE_CHANGE);
__clk_recalc_accuracies(clk);
}
out:
clk_prepare_unlock();
return ret;
}
/**
* __clk_speculate_rates
* @clk: first clk in the subtree
* @parent_rate: the "future" rate of clk's parent
*
* Walks the subtree of clks starting with clk, speculating rates as it
* goes and firing off PRE_RATE_CHANGE notifications as necessary.
*
* Unlike clk_recalc_rates, clk_speculate_rates exists only for sending
* pre-rate change notifications and returns early if no clks in the
* subtree have subscribed to the notifications. Note that if a clk does not
* implement the .recalc_rate callback then it is assumed that the clock will
* take on the rate of its parent.
*
* Caller must hold prepare_lock.
*/
static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
{
struct clk *child;
unsigned long new_rate;
int ret = NOTIFY_DONE;
new_rate = clk_recalc(clk, parent_rate); /* 父时钟更新了,则重新计算当前clk频率 */
/* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */
if (clk->notifier_count)
ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); /* 通知频率更新 */
if (ret & NOTIFY_STOP_MASK) {
pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n",
__func__, clk->name, ret);
goto out;
}
/* 通知子时钟要更新频率了 */
hlist_for_each_entry(child, &clk->children, child_node) {
ret = __clk_speculate_rates(child, new_rate);
if (ret & NOTIFY_STOP_MASK)
break;
}
out:
return ret;
}
static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
{
unsigned long flags;
int ret = 0;
struct clk *old_parent;
old_parent = __clk_set_parent_before(clk, parent); /* 更换时钟前要保证父时钟是打开的 */
/* change clock input source */
if (parent && clk->ops->set_parent)
ret = clk->ops->set_parent(clk->hw, p_index); /* 切换clk的父时钟 */
if (ret) {
flags = clk_enable_lock();
clk_reparent(clk, old_parent); /* 跟换失败,则还是用原来的父时钟 */
clk_enable_unlock(flags);
if (clk->prepare_count) {
clk_disable(clk);
clk_disable(parent);
__clk_unprepare(parent);
}
return ret;
}
__clk_set_parent_after(clk, parent, old_parent); /* 关掉原来的父时钟,打开新的父时钟 */
return 0;
}
/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */
static inline 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;
}
/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */
static inline void clk_disable_unprepare(struct clk *clk)
{
clk_disable(clk);
clk_unprepare(clk);
}
可以看到这组时钟控制相关函数最终都是调用的fops里面的enable,disable,prepare、unprepare、recalc_rate、round_rate、determine_rate、set_parent、get_parent函数,这些具体的操纵函数,则是由具体芯片但单独实现。linux的common clock framework只是提供具体框架接口。
int clk_notifier_register(struct clk *clk, struct notifier_block *nb);
int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
这两个notify接口,用于注册/注销 clock rate改变的通知。例如某个driver关心某个clock,期望这个clock的rate改变时,通知到自己,就可以注册一个notify。后面会举个例子详细说明。
/**
* clk_notifier_register - add a clk rate change notifier
* @clk: struct clk * to watch
* @nb: struct notifier_block * with callback info
*
* Request notification when clk's rate changes. This uses an SRCU
* notifier because we want it to block and notifier unregistrations are
* uncommon. The callbacks associated with the notifier must not
* re-enter into the clk framework by calling any top-level clk APIs;
* this will cause a nested prepare_lock mutex.
*
* In all notification cases cases (pre, post and abort rate change) the
* original clock rate is passed to the callback via struct
* clk_notifier_data.old_rate and the new frequency is passed via struct
* clk_notifier_data.new_rate.
*
* clk_notifier_register() must be called from non-atomic context.
* Returns -EINVAL if called with null arguments, -ENOMEM upon
* allocation failure; otherwise, passes along the return value of
* srcu_notifier_chain_register().
*/
int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
{
struct clk_notifier *cn;
int ret = -ENOMEM;
if (!clk || !nb)
return -EINVAL;
clk_prepare_lock();
/* search the list of notifiers for this clk */
list_for_each_entry(cn, &clk_notifier_list, node) /* 检查时钟通知链表中检查有没有这个clk,没有的话就先添加,后注册 */
if (cn->clk == clk)
break;
/* if clk wasn't in the notifier list, allocate new clk_notifier */
if (cn->clk != clk) {
cn = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL);
if (!cn)
goto out;
cn->clk = clk;
srcu_init_notifier_head(&cn->notifier_head);
list_add(&cn->node, &clk_notifier_list); /* 添加时钟注册链表中 */
}
ret = srcu_notifier_chain_register(&cn->notifier_head, nb); /* 注册 */
clk->notifier_count++; /* 累计要通知的数量 */
out:
clk_prepare_unlock();
return ret;
}
/**
* clk_notifier_unregister - remove a clk rate change notifier
* @clk: struct clk *
* @nb: struct notifier_block * with callback info
*
* Request no further notification for changes to 'clk' and frees memory
* allocated in clk_notifier_register.
*
* Returns -EINVAL if called with null arguments; otherwise, passes
* along the return value of srcu_notifier_chain_unregister().
*/
int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
{
struct clk_notifier *cn = NULL;
int ret = -EINVAL;
if (!clk || !nb)
return -EINVAL;
clk_prepare_lock();
list_for_each_entry(cn, &clk_notifier_list, node)
if (cn->clk == clk) /*通知链表找到 */
break;
if (cn->clk == clk) { /* 卸载 */
ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb);
clk->notifier_count--;
/* XXX the notifier code should handle this better */
if (!cn->notifier_head.head) {
srcu_cleanup_notifier_head(&cn->notifier_head);
list_del(&cn->node); /* 从时钟通知链表删除 */
kfree(cn);
}
} else {
ret = -ENOENT;
}
clk_prepare_unlock();
return ret;
}
int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
struct device *dev);
这是一个非主流接口,用于给某个clk起个别名。无论出于何种原因,尽量不要它,保持代码的简洁,是最高原则!
下图就是对上图时钟函数的一个总结
上图中的黄色区域都是clk core所实现的功能,灰色区域是clock驱动开发需要做的事情,而绿色区域是其他device driver需要使用clock时要调用到的clk功能。
结合一个例子(摘录自“Documentation/devicetree/bindings/clock/clock-bindings.txt”),说明driver怎么使用clock通用API。
1)首先,在DTS(device tree source)中,指定device需要使用的clock,如下:
==Clock consumers==
Required properties:
clocks: List of phandle and clock specifier pairs, one pair
for each clock input to the device. Note: if the
clock provider specifies '0' for #clock-cells, then
only the phandle portion of the pair will appear.
Optional properties:
clock-names: List of clock input name strings sorted in the same
order as the clocks property. Consumers drivers
will use clock-names to match clock input names
with clocks specifiers.
clock-ranges: Empty property indicating that child nodes can inherit named
clocks from this node. Useful for bus nodes to provide a
clock to their children.
For example:
device {
clocks = <&osc 1>, <&ref 0>;
clock-names = "baud", "register";
};
This represents a device with two clock inputs, named "baud" and "register".
The baud clock is connected to output 1 of the &osc device, and the register
clock is connected to output 0 of the &ref.
该DTS的含义是:
device需要使用两个clock,“baud”和“regitser”,由clock-names关键字指定;
baud取自“osc”的输出1,register取自“ref”的输出0,由clocks关键字指定。
那么问题来了,clocks关键字中,<&osc 1>样式的字段是怎么来的?是由clock的provider,也就是底层clock driver规定的(具体会在下一篇文章讲述)。所以使用clock时,一定要找clock driver拿相关的信息(一般会放在“Documentation/devicetree/bindings/clock/”目录下)。
2)系统启动后,device tree会解析clock有关的关键字,并将解析后的信息放在platform_device相关的字段中。
3)具体的driver可以在probe时,以clock的名称(不提供也行)为参数,调用clk get接口,获取clock的句柄,然后利用该句柄,可直接进行enable、set rate等操作,如下:
/* driver */
int xxx_probe(struct platform_device *pdev)
{
struct clk *baud_clk;
int ret;
baud_clk = devm_clk_get(&pdev->dev, “baud”);
if (IS_ERR(clk)) {
…
}
ret = clk_prepare_enable(baud_clk);
if (ret) {
…
}
}
https://blog.csdn.net/rikeyone/article/details/51672720