1. 前言
前面两篇clock framework的分析文章,分别从clock consumer和clock provider的角度,介绍了Linux kernel怎么管理系统的clock资源,以及device driver怎么使用clock资源。本文将深入到clock framework的内部,分析相关的实现逻辑。
注:本文使用的kernel版本为linux-3.16.57。虽然最新版本的kernel增加了一些内容,但主要逻辑没有改变,就不紧跟kernel的步伐了。
2. struct clk结构
到目前为止,我们还没有仔细介绍过struct clk这个代表了一个clock的数据结构呢。对consumer和provider来说,可以不关心,但对内部实现逻辑来说,就不得不提了:
struct clk {
const char *name;
const struct clk_ops *ops;
struct clk_hw *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;
};
name, ops, hw, parents_name, num_parents, flags, 可参考在上一节已经说过;
parent,保存了该clock当前的parent clock的struct clk指针;
parents,一个指针数组,保存了所有可能的parent clock的struct clk指针;
rate,当前的clock rate;
new_rate,新设置的clock rate,之所要保存在这里,是因为set rate过程中有一些中间计算,后面再详解;
enable_count, prepare_count,该clock被enable和prepare的次数,用于确保enable/disable以及prepare/unprepare的成对调用;
children,该clock的children clocks(孩儿们),以链表的形式组织;
child_node,一个list node,自己作为child时,挂到parent的children list时使用;
notifier_count,记录注册到notifier的个数。
3. clock regitser/unregister
在上一节中分析过,clock provider需要将系统的clock以tree的形式组织起来,分门别类,并在系统初始化时,通过provider的初始化接口,或者clock framework core的DTS接口,将所有的clock注册到kernel。
clock的注册,统一由clk_regitser接口实现,但基于该接口,kernel也提供了其它更为便利注册接口,下面将会一一描述。
3.1 clk_regitser
clk_register是所有register接口的共同实现,负责将clock注册到kernel,并返回代表该clock的struct clk指针。分析该接口之前,我们先看一下下面的内容:
上面是kernel中clk_register接口可能的实现位置,由此可以看出,clk_register在“include/linux/clk-provider.h”中声明,却可能在不同的C文件中实现。其它clock API也类似。这说明了什么?
这恰恰呼应了“Linux common clock framework”中“common”一词。
在旧的kernel中,clock framework只是规定了一系列的API声明,具体API的实现,由各个machine代码完成。这就导致每个machine目录下,都有一个类似clock.c的文件,以比较相似的逻辑,实现clock provider的功能。显然,这里面有很多冗余代码。
后来,kernel将这些公共代码,以clock provider的形式(上面drivers/clk/clk.c文件)抽象出来,就成了我们所说的common clock framework。
后面所有的描述,都会以common clock framework的核心代码为基础,其它的,就不再涉及了。
下面是clk_register的实现:
/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the primary interface for populating the clock tree with new
* clock nodes. It returns a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjuction with the
* rest of the clock API. In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
int i, ret;
struct clk *clk;
clk = kzalloc(sizeof(*clk), GFP_KERNEL); /* 申请一个clk */
if (!clk) {
pr_err("%s: could not allocate clk\n", __func__);
ret = -ENOMEM;
goto fail_out;
}
clk->name = kstrdup(hw->init->name, GFP_KERNEL); /* 把hw里面的name重新拷贝一份给clk的name */
if (!clk->name) {
pr_err("%s: could not allocate clk->name\n", __func__);
ret = -ENOMEM;
goto fail_name;
}
clk->ops = hw->init->ops; /* clk绑定ops */
if (dev && dev->driver)
clk->owner = dev->driver->owner;
clk->hw = hw; /* clk绑定hw */
clk->flags = hw->init->flags;
clk->num_parents = hw->init->num_parents; /* clk绑定mux的所有partnt */
hw->clk = clk; /* hw绑定clk */
/* 给num个parent申请num个指针,用来保存所有的paerent */
/* allocate local copy in case parent_names is __initdata */
clk->parent_names = kcalloc(clk->num_parents, sizeof(char *),
GFP_KERNEL);
if (!clk->parent_names) {
pr_err("%s: could not allocate clk->parent_names\n", __func__);
ret = -ENOMEM;
goto fail_parent_names;
}
/* 把hw中所有的parent的name都重新拷贝一份绑定到新申请的clk上 */
/* copy each string name in case parent_names is __initdata */
for (i = 0; i < clk->num_parents; i++) {
clk->parent_names[i] = kstrdup(hw->init->parent_names[i],
GFP_KERNEL);
if (!clk->parent_names[i]) {
pr_err("%s: could not copy parent_names\n", __func__);
ret = -ENOMEM;
goto fail_parent_names_copy;
}
}
ret = __clk_init(dev, clk); /* 校验fops,执行init函数 */
if (!ret)
return clk;
fail_parent_names_copy:
while (--i >= 0)
kfree(clk->parent_names[i]);
kfree(clk->parent_names);
fail_parent_names:
kfree(clk->name);
fail_name:
kfree(clk);
fail_out:
return ERR_PTR(ret);
}
该接口接受一个struct clk_hw指针,该指针包含了将要注册的clock的信息(具体可参考上一节),在内部分配一个struct clk变量后,将clock信息保存在变量中,并返回给调用者。实现逻辑如下:
分配struct clk空间;
根据struct clk_hw指针提供的信息,初始化clk的name、ops、hw、flags、num_parents、parents_names等变量;
调用内部接口__clk_init,执行后续的初始化操作。这个接口包含了clk_regitser的主要逻辑,具体如下。
/**
* __clk_init - initialize the data structures in a struct clk
* @dev: device initializing this clk, placeholder for now
* @clk: clk being initialized
*
* Initializes the lists in struct clk, queries the hardware for the
* parent and rate and sets them both.
*/
int __clk_init(struct device *dev, struct clk *clk)
{
int i, ret = 0;
struct clk *orphan;
struct hlist_node *tmp2;
if (!clk)
return -EINVAL;
clk_prepare_lock(); //获取锁
/* check to see if a clock with this name is already registered */
/*校验当前注册的时钟是否已经注册过了*/
if (__clk_lookup(clk->name)) {
pr_debug("%s: clk %s already initialized\n",
__func__, clk->name);
ret = -EEXIST;
goto out;
}
/* check that clk_ops are sane. See Documentation/clk.txt */
/* 检查ops里面是否有相应clk的计算函数 */
if (clk->ops->set_rate &&
!((clk->ops->round_rate || clk->ops->determine_rate) &&
clk->ops->recalc_rate)) {
pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
__func__, clk->name);
ret = -EINVAL;
goto out;
}
/* 校验该clk的ops里面是否由设置和得到当前时钟的接口 */
if (clk->ops->set_parent && !clk->ops->get_parent) {
pr_warning("%s: %s must implement .get_parent & .set_parent\n",
__func__, clk->name);
ret = -EINVAL;
goto out;
}
/* 校验如果有set_rate_and_parent ,则set_parent和set_rate也必须有,因为里面调用这两个了 */
if (clk->ops->set_rate_and_parent &&
!(clk->ops->set_parent && clk->ops->set_rate)) {
pr_warn("%s: %s must implement .set_parent & .set_rate\n",
__func__, clk->name);
ret = -EINVAL;
goto out;
}
/* throw a WARN if any entries in parent_names are NULL */
/* 检查parent不是NULL */
for (i = 0; i < clk->num_parents; i++)
WARN(!clk->parent_names[i],
"%s: invalid NULL in %s's .parent_names\n",
__func__, clk->name);
/*
* Allocate an array of struct clk *'s to avoid unnecessary string
* look-ups of clk's possible parents. This can fail for clocks passed
* in to clk_init during early boot; thus any access to clk->parents[]
* must always check for a NULL pointer and try to populate it if
* necessary.
*
* If clk->parents is not NULL we skip this entire block. This allows
* for clock drivers to statically initialize clk->parents.
*/
/* 如果父时钟个数大于1个,且目前没绑定好paernt,则查找到并绑定到clk的parents中 */
if (clk->num_parents > 1 && !clk->parents) {
clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *),
GFP_KERNEL);
/*
* __clk_lookup returns NULL for parents that have not been
* clk_init'd; thus any access to clk->parents[] must check
* for a NULL pointer. We can always perform lazy lookups for
* missing parents later on.
*/
/* 从clk tree中根据parent_names中的每个name查找parent,并绑定到clk->parents数组中 */
if (clk->parents)
for (i = 0; i < clk->num_parents; i++)
clk->parents[i] =
__clk_lookup(clk->parent_names[i]);
}
clk->parent = __clk_init_parent(clk); /* 得到parent */
/*
* Populate clk->parent if parent has already been __clk_init'd. If
* parent has not yet been __clk_init'd then place clk in the orphan
* list. If clk has set the CLK_IS_ROOT flag then place it in the root
* clk list.
*
* Every time a new clk is clk_init'd then we walk the list of orphan
* clocks and re-parent any that are children of the clock currently
* being clk_init'd.
*/
/*根据时钟类型的不同,注册到不同的链表中*/
if (clk->parent)
hlist_add_head(&clk->child_node,
&clk->parent->children);
else if (clk->flags & CLK_IS_ROOT)
hlist_add_head(&clk->child_node, &clk_root_list);
else
hlist_add_head(&clk->child_node, &clk_orphan_list);
/*
* Set clk's accuracy. The preferred method is to use
* .recalc_accuracy. For simple clocks and lazy developers the default
* fallback is to use the parent's accuracy. If a clock doesn't have a
* parent (or is orphaned) then accuracy is set to zero (perfect
* clock).
*/
//设置clk的准确性
if (clk->ops->recalc_accuracy)
clk->accuracy = clk->ops->recalc_accuracy(clk->hw,
__clk_get_accuracy(clk->parent));
else if (clk->parent)
clk->accuracy = clk->parent->accuracy;
else
clk->accuracy = 0;
/*
* Set clk's rate. The preferred method is to use .recalc_rate. For
* simple clocks and lazy developers the default fallback is to use the
* parent's rate. If a clock doesn't have a parent (or is orphaned)
* then rate is set to zero.
*/
//重新计算时钟频率
if (clk->ops->recalc_rate)
clk->rate = clk->ops->recalc_rate(clk->hw,
__clk_get_rate(clk->parent));
else if (clk->parent)
clk->rate = clk->parent->rate;
else
clk->rate = 0;
clk_debug_register(clk);
/*
* walk the list of orphan clocks and reparent any that are children of
* this clock
*/
//遍历孤儿时钟列表并重新显示此时钟的任何孩子
hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
if (orphan->num_parents && orphan->ops->get_parent) {
i = orphan->ops->get_parent(orphan->hw);
if (!strcmp(clk->name, orphan->parent_names[i]))
__clk_reparent(orphan, clk);
continue;
}
for (i = 0; i < orphan->num_parents; i++)
if (!strcmp(clk->name, orphan->parent_names[i])) {
__clk_reparent(orphan, clk);
break;
}
}
/*
* optional platform-specific magic
*
* The .init callback is not used by any of the basic clock types, but
* exists for weird hardware that must perform initialization magic.
* Please consider other ways of solving initialization problems before
* using this callback, as its use is discouraged.
*/
//如果clock ops提供了init接口,执行之(由注释可知,kernel不建议提供init接口)。
if (clk->ops->init)
clk->ops->init(clk->hw);
kref_init(&clk->ref);
out:
clk_prepare_unlock();
return ret;
}
__clk_init接口的实现相当繁琐,做的事情包括:
21~27行,以clock name为参数,调用__clk_lookup接口,查找是否已有相同name的clock注册,如果有,则返回错误。由此可以看出,clock framework以name唯一识别一个clock,因此不能有同名的clock存在;
29~55行,检查clk ops的完整性,例如:如果提供了set_rate接口,就必须提供round_rate和recalc_rate接口;如果提供了set_parent,就必须提供get_parent。这些逻辑背后的含义,会在后面相应的地方详细描述;
57~89行,分配一个struct clk *类型的数组,缓存该clock的parents clock。具体方法是根据parents_name,查找相应的struct clk指针;
91行,获取当前的parent clock,并将其保存在parent指针中。具体可参考下面“说明2”;
93~110行,根据该clock的特性,将它添加到clk_root_list、clk_orphan_list或者parent->children三个链表中的一个,具体请参考下面“说明1”;
112~126行,计算时钟的准确性
118~141行,计算clock的初始rate,具体请参考下面“说明3”;
148~162行,尝试reparent当前所有的孤儿(orphan)clock,具体请参考下面“说明4”;
164~174行,如果clock ops提供了init接口,执行之(由注释可知,kernel不建议提供init接口)。
上面的clock init流程,有下面4点补充说明:
说明1:clock的管理和查询
static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);
clock framework有2条全局的链表:clk_root_list和clk_orphan_list。所有设置了CLK_IS_ROOT属性的clock都会挂在clk_root_list中。其它clock,如果有valid的parent ,则会挂到parent的“children”链表中,如果没有valid的parent,则会挂到clk_orphan_list中。
查询时(__clk_lookup接口做的事情),依次搜索:clk_root_list-->root_clk-->children-->child's children,clk_orphan_list-->orphan_clk-->children-->child's children,即可。
说明2:当前parent clock的选择(__clk_init_parent)
对于没有parent,或者只有1个parent 的clock来说,比较简单,设置为NULL,或者根据parent name获得parent的struct clk指针接。
对于有多个parent的clock,就必须提供.get_parent ops,该ops要根据当前硬件的配置情况,例如寄存器值,返回当前所有使用的parent的index(即第几个parent)。然后根据index,取出对应parent clock的struct clk指针,作为当前的parent。
说明3:clock的初始rate计算
对于提供.recalc_rate ops的clock来说,优先使用该ops获取初始的rate。如果没有提供,退而求其次,直接使用parent clock的rate。最后,如果该clock没有parent,则初始的rate只能选择为0。
.recalc_rate ops的功能,是以parent clock的rate为输入参数,根据当前硬件的配置情况,如寄存器值,计算获得自身的rate值。
说明4:orphan clocks的reparen
有些情况下,child clock会先于parent clock注册,此时该child就会成为orphan clock,被收养在clk_orphan_list中。
而每当新的clock注册时,kernel都会检查这个clock是否是某个orphan的parent,如果是,就把这个orphan从clk_orphan_list中移除,放到新注册的clock的怀抱。这就是reparent的功能,它的处理逻辑是:
a) 遍历orphan list,如果orphan提供了.get_parent ops,则通过该ops得到当前parent的index,并从parent_names中取出该parent的name,然后和新注册的clock name比较,如果相同,呵呵,找到parent了,执行__clk_reparent,进行后续的操作。
b) 如果没有提供.get_parent ops,只能遍历自己的parent_names,检查是否有和新注册clock匹配的,如果有,执行__clk_reparent,进行后续的操作。
c) __clk_reparent会把这个orphan从clk_orphan_list中移除,并挂到新注册的clock上。然后调用__clk_recalc_rates,重新计算自己以及自己所有children的rate。计算过程和上面的clock rate设置类似。
3.2 clk_unregister/devm_clk_register/devm_clk_unregister
clock的regitser和init,几乎占了clock framework大部分的实现逻辑。clk_unregister是regitser接口的反操作。而devm_clk_register/devm_clk_unregister则是clk_register/clk_unregister的device resource manager版本。
/**
* clk_unregister - unregister a currently registered clock
* @clk: clock to unregister
*/
void clk_unregister(struct clk *clk)
{
unsigned long flags;
if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
return;
clk_prepare_lock();
if (clk->ops == &clk_nodrv_ops) { /* 检查是否已经卸载的 */
pr_err("%s: unregistered clock: %s\n", __func__, clk->name);
goto out;
}
/*
* Assign empty clock ops for consumers that might still hold
* a reference to this clock.
*/
flags = clk_enable_lock();
clk->ops = &clk_nodrv_ops; /* 已经卸载的会用空的drv来标记 */
clk_enable_unlock(flags);
if (!hlist_empty(&clk->children)) { /* 如果有子clk,则该clk卸载后,它的所有子clk都要设置成孤儿 */
struct clk *child;
struct hlist_node *t;
/* Reparent all children to the orphan list. */
hlist_for_each_entry_safe(child, t, &clk->children, child_node)
clk_set_parent(child, NULL);
}
clk_debug_unregister(clk);
hlist_del_init(&clk->child_node); /* 删除 */
if (clk->prepare_count)
pr_warn("%s: unregistering prepared clock: %s\n",
__func__, clk->name);
kref_put(&clk->ref, __clk_release);
out:
clk_prepare_unlock();
}
/**
* devm_clk_register - resource managed clk_register()
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* Managed clk_register(). Clocks returned from this function are
* automatically clk_unregister()ed on driver detach. See clk_register() for
* more information.
*/
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw)
{
struct clk *clk;
struct clk **clkp;
clkp = devres_alloc(devm_clk_release, sizeof(*clkp), GFP_KERNEL);
if (!clkp)
return ERR_PTR(-ENOMEM);
clk = clk_register(dev, hw);
if (!IS_ERR(clk)) {
*clkp = clk;
devres_add(dev, clkp);
} else {
devres_free(clkp);
}
return clk;
}
/**
* devm_clk_unregister - resource managed clk_unregister()
* @clk: clock to unregister
*
* Deallocate a clock allocated with devm_clk_register(). Normally
* this function will not need to be called and the resource management
* code will ensure that the resource is freed.
*/
void devm_clk_unregister(struct device *dev, struct clk *clk)
{
WARN_ON(devres_release(dev, devm_clk_release, devm_clk_match, clk));
}
static void devm_clk_release(struct device *dev, void *res)
{
clk_unregister(*(struct clk **)res);
}
3.3 fixed rate clock的注册
上一节中已经对fixed rate clock有过详细的介绍,这种类型的clock有两种注册方式,通过API注册和通过DTS注册,具体的实现位于“drivers/clk/clk-fixed-rate.c”中,介绍如下。
/**
* clk_register_fixed_rate - register fixed-rate clock with the clock framework
* @dev: device that is registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @fixed_rate: non-adjustable clock rate
*/
//注册固定时钟频率
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned long fixed_rate)
{
return clk_register_fixed_rate_with_accuracy(dev, name, parent_name,
flags, fixed_rate, 0);
}
/**
* clk_register_fixed_rate_with_accuracy - register fixed-rate clock with the
* clock framework
* @dev: device that is registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @fixed_rate: non-adjustable clock rate
* @fixed_accuracy: non-adjustable clock rate
*/
struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned long fixed_rate, unsigned long fixed_accuracy)
{
struct clk_fixed_rate *fixed;
struct clk *clk;
struct clk_init_data init;
/* allocate fixed-rate clock */
fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); //分配一个固定系数的时钟
if (!fixed) {
pr_err("%s: could not allocate fixed clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clk_fixed_rate_ops; /* 绑定固定时钟的ops,见下面 */
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name: NULL); /* 这种只有一个parent或没有 */
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_fixed_rate assignments */
fixed->fixed_rate = fixed_rate; /* 固定速率 */
fixed->fixed_accuracy = fixed_accuracy; /* 固定精度 */
fixed->hw.init = &init;
/* register the clock */
clk = clk_register(dev, &fixed->hw); /* 注册固定评论的clk */
if (IS_ERR(clk))
kfree(fixed);
return clk;
}
说明1:struct clk_init_data类型的变量
struct clk_init_data类型的变量(init),是一个局部变量,传递给clk_regitser使用时,用的是它的指针,说明了什么?说明该变量不会再后面使用了。再回忆一下clk_regitser的实现逻辑,会把所有的信息copy一遍,这里就好理解了。后面其它类型的clock注册时,道理相同。
说明2:fixed rate clock的实现思路
私有数据结构的定义如下:
/**
* struct clk_fixed_rate - fixed-rate clock
* @hw: handle between common and hardware-specific interfaces
* @fixed_rate: constant frequency of clock
*/
struct clk_fixed_rate {
struct clk_hw hw;
unsigned long fixed_rate;
unsigned long fixed_accuracy;
u8 flags;
};
包含一个struct clk_hw变量,用于clk_regitser。另外三个变量,则为该类型clock特有的属性。私有数据结构变量(fixed)是通过kzalloc分配的,说明后续还需要使用。那怎么用呢?
由clk_regitser的实现可知,fixed rate clock注册时hw);>,把fixed指针中hw变量的地址保存在了struct clk指针中了。因此,在任何时候,通过struct clk指针(clock的代表),就可以找到所对应clock的struct clk_hw指针,从而可以找到相应的私有变量(fixed)的指针以及其中的私有数据。
基于此,fixed rate ops的实现就顺利成章了:
/*
* DOC: basic fixed-rate clock that cannot gate
*
* Traits of this clock:
* prepare - clk_(un)prepare only ensures parents are prepared
* enable - clk_enable only ensures parents are enabled
* rate - rate is always a fixed value. No clk_set_rate support
* parent - fixed parent. No clk_set_parent support
*/
#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw)
static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return to_clk_fixed_rate(hw)->fixed_rate; /* 固定时钟频率 */
}
static unsigned long clk_fixed_rate_recalc_accuracy(struct clk_hw *hw,
unsigned long parent_accuracy)
{
return to_clk_fixed_rate(hw)->fixed_accuracy; /* 固定时钟精度 */
}
const struct clk_ops clk_fixed_rate_ops = {
.recalc_rate = clk_fixed_rate_recalc_rate,
.recalc_accuracy = clk_fixed_rate_recalc_accuracy,
};
2)通过DTS注册
fixed rate clock是非常简单的一种clock,因而可以直接通过DTS的形式注册,clock framework负责解析DTS,并调用API注册clock,如下:
#ifdef CONFIG_OF
/**
* of_fixed_clk_setup() - Setup function for simple fixed rate clock
*/
void of_fixed_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
u32 rate;
u32 accuracy = 0;
if (of_property_read_u32(node, "clock-frequency", &rate))
return;
of_property_read_u32(node, "clock-accuracy", &accuracy);
of_property_read_string(node, "clock-output-names", &clk_name);
clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
CLK_IS_ROOT, rate,
accuracy);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
#endif
#define CLK_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn)
#define OF_DECLARE_1(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_1)
#ifdef CONFIG_OF
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section(__##table##_of_table) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#else
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__attribute__((unused)) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#endif
首先看一下CLK_OF_DECLARE宏,它的定义位于“include/linux/clk-provider.h”中,负责在指定的section中(以__clk_of_table开始的位置),定义struct of_device_id类型的变量,并由of_clk_init接口解析、匹配,如果匹配成功,则执行相应的回调函数(这里为of_fixed_clk_setup);
初始化的时候,device tree负责读取DTS,并和这些变量的名字(这里为"fixed-clock")匹配,如果匹配成功,则执行相应的回调函数(这里为of_fixed_clk_setup);
of_fixed_clk_setup会解析两个DTS字段"clock-frequency"和"clock-output-names",然后调用clk_register_fixed_rate,注册clock。注意,注册时的flags为CLK_IS_ROOT,说明目前只支持ROOT类型的clock通过DTS注册;
最后,调用of_clk_add_provider接口,将该clock添加到provider list中,方便后续的查找使用。该接口会在后面再详细介绍。
of_clk_init负责从DTS中扫描并初始化clock provider,如下:
/**
* of_clk_init() - Scan and init clock providers from the DT
* @matches: array of compatible values and init functions for providers.
*
* This function scans the device tree for matching clock providers
* and calls their initialization functions. It also does it by trying
* to follow the dependencies.
*/
void __init of_clk_init(const struct of_device_id *matches)
{
const struct of_device_id *match;
struct device_node *np;
struct clock_provider *clk_provider, *next;
bool is_init_done;
bool force = false;
if (!matches)
matches = &__clk_of_table;
/* First prepare the list of the clocks providers */
for_each_matching_node_and_match(np, matches, &match) {
struct clock_provider *parent =
kzalloc(sizeof(struct clock_provider), GFP_KERNEL);
parent->clk_init_cb = match->data;
parent->np = np;
list_add_tail(&parent->node, &clk_provider_list);
}
while (!list_empty(&clk_provider_list)) {
is_init_done = false;
list_for_each_entry_safe(clk_provider, next,
&clk_provider_list, node) {
if (force || parent_ready(clk_provider->np)) {
clk_provider->clk_init_cb(clk_provider->np);
list_del(&clk_provider->node);
kfree(clk_provider);
is_init_done = true;
}
}
/*
* We didn't manage to initialize any of the
* remaining providers during the last loop, so now we
* initialize all the remaining ones unconditionally
* in case the clock parent was not mandatory
*/
if (!is_init_done)
force = true;
}
}
该接口有一个输入参数,用于指定需要扫描的OF id,如果留空,则会扫描__clk_of_table,就是通过CLK_OF_DECLARE宏指定的fixed rate等类型的clock。
在最新的kernel中,会在初始化代码(time_init)中以NULL为参数调用一次of_clk_init,以便自动匹配并初始化DTS中的描述的类似fixed rate的clock。
这里使用大量篇幅描述一个简单的fixed rate clock的注册方式,主要目的是给大家介绍一种通用的实现方式,或者说通用思路。后面其它类型的clock,包括我们自定义的类型,实现方法都是一样的。这里就不罗嗦了,大家看代码就可以了。
3.4 gate、devider、mux、fixed factor、composite以及自定义类型clock的注册
这里先放有一个时钟分频器(devider)的驱动底层实现
/**
* clk_register_divider - register a divider clock with the clock framework
* @dev: device registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @reg: register address to adjust divider
* @shift: number of bits to shift the bitfield
* @width: width of the bitfield
* @clk_divider_flags: divider-specific flags for this clock
* @lock: shared register lock for this clock
*/
struct clk *clk_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, spinlock_t *lock)
{
return _register_divider(dev, name, parent_name, flags, reg, shift,
width, clk_divider_flags, NULL, lock);
}
EXPORT_SYMBOL_GPL(clk_register_divider);
/**
* clk_register_divider_table - register a table based divider clock with
* the clock framework
* @dev: device registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @reg: register address to adjust divider
* @shift: number of bits to shift the bitfield
* @width: width of the bitfield
* @clk_divider_flags: divider-specific flags for this clock
* @table: array of divider/value pairs ending with a div set to 0
* @lock: shared register lock for this clock
*/
struct clk *clk_register_divider_table(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock)
{
return _register_divider(dev, name, parent_name, flags, reg, shift,
width, clk_divider_flags, table, lock);
}
static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock)
{
struct clk_divider *div;
struct clk *clk;
struct clk_init_data init;
if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
if (width + shift > 16) {
pr_warn("divider value exceeds LOWORD field\n");
return ERR_PTR(-EINVAL);
}
}
/* allocate the divider */
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
if (!div) {
pr_err("%s: could not allocate divider clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clk_divider_ops; /* 分频器操作接口 */
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_divider assignments */
div->reg = reg; /* 分频器寄存器地址 */
div->shift = shift; /* 分频器从那一位开始 */
div->width = width; /* 分频器位宽 */
div->flags = clk_divider_flags;
div->lock = lock;
div->hw.init = &init;
div->table = table;
/* register the clock */
clk = clk_register(dev, &div->hw);
if (IS_ERR(clk))
kfree(div);
return clk;
}
因为分频器分频可能涉及到不止2分频,还可能1、2、4...很多分频的情况,注册时会注册对应值以及分频比,同时也会有读写寄存器,以及按需求设置最接近要求的频率的计算和设置,需要设置某个频率,最终是通过查表来确认分频值,所以分频器属于比较复杂的时钟,代码会比较多,但思路和固定频率的完全一样。
/*
* DOC: basic adjustable divider clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor)
* parent - fixed parent. No clk_set_parent support
*/
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
#define div_mask(d) ((1 << ((d)->width)) - 1)
static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
{
unsigned int maxdiv = 0;
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->div > maxdiv)
maxdiv = clkt->div;
return maxdiv;
}
static unsigned int _get_table_mindiv(const struct clk_div_table *table)
{
unsigned int mindiv = UINT_MAX;
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->div < mindiv)
mindiv = clkt->div;
return mindiv;
}
static unsigned int _get_maxdiv(struct clk_divider *divider)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return div_mask(divider);
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << div_mask(divider);
if (divider->table)
return _get_table_maxdiv(divider->table);
return div_mask(divider) + 1;
}
static unsigned int _get_table_div(const struct clk_div_table *table,
unsigned int val)
{
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->val == val)
return clkt->div;
return 0;
}
static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return val;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << val;
if (divider->table)
return _get_table_div(divider->table, val);
return val + 1;
}
static unsigned int _get_table_val(const struct clk_div_table *table,
unsigned int div)
{
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->div == div)
return clkt->val;
return 0;
}
static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return div;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return __ffs(div);
if (divider->table)
return _get_table_val(divider->table, div);
return div - 1;
}
static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int div, val;
val = clk_readl(divider->reg) >> divider->shift;
val &= div_mask(divider);
div = _get_div(divider, val);
if (!div) {
WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
__clk_get_name(hw->clk));
return parent_rate;
}
return DIV_ROUND_UP(parent_rate, div);
}
static bool _is_valid_table_div(const struct clk_div_table *table,
unsigned int div)
{
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
if (clkt->div == div)
return true;
return false;
}
static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
{
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return is_power_of_2(div);
if (divider->table)
return _is_valid_table_div(divider->table, div);
return true;
}
static int _round_up_table(const struct clk_div_table *table, int div)
{
const struct clk_div_table *clkt;
int up = INT_MAX;
for (clkt = table; clkt->div; clkt++) {
if (clkt->div == div)
return clkt->div;
else if (clkt->div < div)
continue;
if ((clkt->div - div) < (up - div))
up = clkt->div;
}
return up;
}
static int _round_down_table(const struct clk_div_table *table, int div)
{
const struct clk_div_table *clkt;
int down = _get_table_mindiv(table);
for (clkt = table; clkt->div; clkt++) {
if (clkt->div == div)
return clkt->div;
else if (clkt->div > div)
continue;
if ((div - clkt->div) < (div - down))
down = clkt->div;
}
return down;
}
static int _div_round_up(struct clk_divider *divider,
unsigned long parent_rate, unsigned long rate)
{
int div = DIV_ROUND_UP(parent_rate, rate);
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
div = __roundup_pow_of_two(div);
if (divider->table)
div = _round_up_table(divider->table, div);
return div;
}
static int _div_round_closest(struct clk_divider *divider,
unsigned long parent_rate, unsigned long rate)
{
int up, down, div;
unsigned long up_rate, down_rate;
up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) {
up = __roundup_pow_of_two(div);
down = __rounddown_pow_of_two(div);
} else if (divider->table) {
up = _round_up_table(divider->table, div);
down = _round_down_table(divider->table, div);
}
up_rate = DIV_ROUND_UP(parent_rate, up);
down_rate = DIV_ROUND_UP(parent_rate, down);
return (rate - up_rate) <= (down_rate - rate) ? up : down;
}
static int _div_round(struct clk_divider *divider, unsigned long parent_rate,
unsigned long rate)
{
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
return _div_round_closest(divider, parent_rate, rate);
return _div_round_up(divider, parent_rate, rate);
}
static bool _is_best_div(struct clk_divider *divider,
unsigned long rate, unsigned long now, unsigned long best)
{
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
return abs(rate - now) < abs(rate - best);
return now <= rate && now > best;
}
static int _next_div(struct clk_divider *divider, int div)
{
div++;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return __roundup_pow_of_two(div);
if (divider->table)
return _round_up_table(divider->table, div);
return div;
}
static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
int i, bestdiv = 0;
unsigned long parent_rate, best = 0, now, maxdiv;
unsigned long parent_rate_saved = *best_parent_rate;
if (!rate)
rate = 1;
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
bestdiv = clk_readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider);
bestdiv = _get_div(divider, bestdiv);
return bestdiv;
}
maxdiv = _get_maxdiv(divider);
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
bestdiv = _div_round(divider, parent_rate, rate);
bestdiv = bestdiv == 0 ? 1 : bestdiv;
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
return bestdiv;
}
/*
* The maximum divider we can use without overflowing
* unsigned long in rate * i below
*/
maxdiv = min(ULONG_MAX / rate, maxdiv);
for (i = 1; i <= maxdiv; i = _next_div(divider, i)) {
if (!_is_valid_div(divider, i))
continue;
if (rate * i == parent_rate_saved) {
/*
* It's the most ideal case if the requested rate can be
* divided from parent clock without needing to change
* parent rate, so return the divider immediately.
*/
*best_parent_rate = parent_rate_saved;
return i;
}
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
rate * i);
now = DIV_ROUND_UP(parent_rate, i);
if (_is_best_div(divider, rate, now, best)) {
bestdiv = i;
best = now;
*best_parent_rate = parent_rate;
}
}
if (!bestdiv) {
bestdiv = _get_maxdiv(divider);
*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
}
return bestdiv;
}
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
int div;
div = clk_divider_bestdiv(hw, rate, prate);
return DIV_ROUND_UP(*prate, div);
}
static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int div, value;
unsigned long flags = 0;
u32 val;
div = DIV_ROUND_UP(parent_rate, rate);
if (!_is_valid_div(divider, div))
return -EINVAL;
value = _get_val(divider, div);
if (value > div_mask(divider))
value = div_mask(divider);
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
val = div_mask(divider) << (divider->shift + 16);
} else {
val = clk_readl(divider->reg);
val &= ~(div_mask(divider) << divider->shift);
}
val |= value << divider->shift;
clk_writel(val, divider->reg);
if (divider->lock)
spin_unlock_irqrestore(divider->lock, flags);
return 0;
}
const struct clk_ops clk_divider_ops = {
.recalc_rate = clk_divider_recalc_rate, /* 根据父时钟计算频率 */
.round_rate = clk_divider_round_rate, /* 计算最接近要求频率的设置 */
.set_rate = clk_divider_set_rate, /* 读写寄存器,设置频率 */
};
其他几个的和这个类似,就不再这里说明了。
4.时钟查找表的注册
从前面分析,clk本来是以链表的形式组成树的形式的(下节给图)。但树的形式对查找来说就不是很方便了,所以内核中,又对五种形式的时钟,以clk_lookup_alloc组织的链表的形式管理了起来。
下面是管理链表的定义
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
下面看一下是怎么组织的,后面章节讲使用。
/**
* clk_register_clkdev - register one clock lookup for a struct clk
* @clk: struct clk to associate with all clk_lookups
* @con_id: connection ID string on device
* @dev_id: format string describing device name
*
* con_id or dev_id may be NULL as a wildcard, just as in the rest of
* clkdev.
*
* To make things easier for mass registration, we detect error clks
* from a previous clk_register() call, and return the error code for
* those. This is to permit this function to be called immediately
* after clk_register().
*/
int clk_register_clkdev(struct clk *clk, const char *con_id,
const char *dev_fmt, ...)
{
struct clk_lookup *cl;
va_list ap;
if (IS_ERR(clk))
return PTR_ERR(clk);
va_start(ap, dev_fmt);
cl = vclkdev_alloc(clk, con_id, dev_fmt, ap); /* clk绑定到clk_lookup,同时把con_id和参数dev_fmt绑定好 */
va_end(ap);
if (!cl)
return -ENOMEM;
clkdev_add(cl); /* 把cl以加入clock链表中 */
return 0;
}
struct clk_lookup_alloc {
struct clk_lookup cl;
char dev_id[MAX_DEV_ID];
char con_id[MAX_CON_ID];
};
struct clk_lookup {
struct list_head node;
const char *dev_id;
const char *con_id;
struct clk *clk;
};
static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
{
return kzalloc(size, GFP_KERNEL);
}
static struct clk_lookup * __init_refok
vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
va_list ap)
{
struct clk_lookup_alloc *cla;
cla = __clkdev_alloc(sizeof(*cla)); /* 申请clk_lookup_alloc空间 */
if (!cla)
return NULL;
cla->cl.clk = clk; /* clk绑定到clk_lookup_alloc 里面的clk_lookup上 */
if (con_id) {
strlcpy(cla->con_id, con_id, sizeof(cla->con_id)); /* 把用户传的con_id绑定到clk_lookup_alloc的con_id里 */
cla->cl.con_id = cla->con_id; /* 同时clk_lookup 也绑定相同的 */
}
if (dev_fmt) {
/* 同上,绑定dev_id */
vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
cla->cl.dev_id = cla->dev_id;
}
return &cla->cl; /* 返回clk_lookup,后面的使用也是用的clk_lookup,不用clk_lookup_alloc */
}
/* 绑定clk_lookup 到clock链表上 */
void clkdev_add(struct clk_lookup *cl)
{
mutex_lock(&clocks_mutex);
list_add_tail(&cl->node, &clocks);
mutex_unlock(&clocks_mutex);
}
当然有注册也必须有删除的,但是这个是没有用的,时钟属于cpu中的硬件,可以关掉,不能删除。
/*
* clkdev_drop - remove a clock dynamically allocated
*/
void clkdev_drop(struct clk_lookup *cl)
{
mutex_lock(&clocks_mutex);
list_del(&cl->node);
mutex_unlock(&clocks_mutex);
kfree(cl);
}
前面时把时钟注册到clock链表中。
下面就是使用了。
先看一下使用方式。
现在内核都是和设备资源管理一块来使用的,但核心的函数都是一样的。
struct clk *devm_clk_get(struct device *dev, const char *id)
{
struct clk **ptr, *clk;
ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); /* 申请绑定自动释放设备 */
if (!ptr)
return ERR_PTR(-ENOMEM);
clk = clk_get(dev, id); /* 得到时钟 */
if (!IS_ERR(clk)) {
*ptr = clk;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return 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;
}
可以看到最终就是根据con_id或dev_id来从clock链表中搜索匹配,找到最恰当的那个clk。
最后说一下,新的4.9.92的内核中,对数据结构做了一些调整。
下面列出不同点3.16.57
/**
* struct clk_hw - handle for traversing from a struct clk to its corresponding
* hardware-specific structure. struct clk_hw should be declared within struct
* clk_foo and then referenced by the struct clk instance that uses struct
* clk_foo's clk_ops
*
* @clk: pointer to the struct clk instance that points back to this struct
* clk_hw instance
*
* @init: pointer to struct clk_init_data that contains the init data shared
* with the common clock framework.
*/
struct clk_hw {
struct clk *clk;
const struct clk_init_data *init;
};
struct clk {
const char *name;
const struct clk_ops *ops;
struct clk_hw *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;
};
4.9.92中把
/**
* struct clk_hw - handle for traversing from a struct clk to its corresponding
* hardware-specific structure. struct clk_hw should be declared within struct
* clk_foo and then referenced by the struct clk instance that uses struct
* clk_foo's clk_ops
*
* @core: pointer to the struct clk_core instance that points back to this
* struct clk_hw instance
*
* @clk: pointer to the per-user struct clk instance that can be used to call
* into the clk API
*
* @init: pointer to struct clk_init_data that contains the init data shared
* with the common clock framework.
*/
struct clk_hw {
struct clk_core *core;
struct clk *clk;
const struct clk_init_data *init;
};
/*** private data structures ***/
struct clk_core {
const char *name;
const struct clk_ops *ops;
struct clk_hw *hw;
struct module *owner;
struct clk_core *parent;
const char **parent_names;
struct clk_core **parents;
u8 num_parents;
u8 new_parent_index;
unsigned long rate;
unsigned long req_rate;
unsigned long new_rate;
struct clk_core *new_parent;
struct clk_core *new_child;
unsigned long flags;
bool orphan;
unsigned int enable_count;
unsigned int prepare_count;
unsigned long min_rate;
unsigned long max_rate;
unsigned long accuracy;
int phase;
struct hlist_head children;
struct hlist_node child_node;
struct hlist_head clks;
unsigned int notifier_count;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
struct hlist_node debug_node;
#endif
struct kref ref;
};
struct clk {
struct clk_core *core;
const char *dev_id;
const char *con_id;
unsigned long min_rate;
unsigned long max_rate;
struct hlist_node clks_node;
};
对比可以看到,把之前clk中一些私有的数据,全部都重新分装了一个结构体。称作clk_core。
同时也增加来时钟速率的最大最小分为限制。
同时clk_hw链接来一个clk的所有有效信息,3.16.57内核还有clk做内部中转,而4.9.98用clk_hw来作为来一个核心中转战。
下面列出4.9.98的核心代码
/**
* clk_register_clkdev - register one clock lookup for a struct clk
* @clk: struct clk to associate with all clk_lookups
* @con_id: connection ID string on device
* @dev_id: string describing device name
*
* con_id or dev_id may be NULL as a wildcard, just as in the rest of
* clkdev.
*
* To make things easier for mass registration, we detect error clks
* from a previous clk_register() call, and return the error code for
* those. This is to permit this function to be called immediately
* after clk_register().
*/
int clk_register_clkdev(struct clk *clk, const char *con_id,
const char *dev_id)
{
struct clk_lookup *cl;
if (IS_ERR(clk))
return PTR_ERR(clk);
/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
*/
/* 下面就是差异点,先从clk中拿到clk_hw再使用 */
if (dev_id)
cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s",
dev_id);
else
cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL);
return cl ? 0 : -ENOMEM;
}
static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
const char *con_id,
const char *dev_id, ...)
{
struct clk_lookup *cl;
va_list ap;
va_start(ap, dev_id);
cl = vclkdev_create(hw, con_id, dev_id, ap); /* 名字做来调整 */
va_end(ap);
return cl;
}
static struct clk_lookup *
vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
va_list ap)
{
struct clk_lookup *cl;
cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
if (cl)
__clkdev_add(cl);
return cl;
}
static struct clk_lookup * __ref
vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
va_list ap)
{
struct clk_lookup_alloc *cla;
cla = __clkdev_alloc(sizeof(*cla));
if (!cla)
return NULL;
cla->cl.clk_hw = hw;
if (con_id) {
strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
cla->cl.con_id = cla->con_id;
}
if (dev_fmt) {
vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
cla->cl.dev_id = cla->dev_id;
}
return &cla->cl;
}
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, dev_id, con_id);
if (!IS_ERR(clk) || 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;
struct clk *clk = NULL;
mutex_lock(&clocks_mutex);
cl = clk_find(dev_id, con_id);
if (!cl)
goto out;
clk = __clk_create_clk(cl->clk_hw, dev_id, con_id); /* 相比前面增加clk申请来管理 */
if (IS_ERR(clk))
goto out;
if (!__clk_get(clk)) {
__clk_free_clk(clk);
cl = NULL;
goto out;
}
out:
mutex_unlock(&clocks_mutex);
return cl ? clk : ERR_PTR(-ENOENT);
}
/*
每个clock由一个struct clk_core描述,其与struct clk_hw是一一对应的关系,
但是struct clk可能有很多个,其他驱动需要操作clock时,都需要先分配一个
struct clk 类型的指针,因此其与struct clk_core是一对多的关系,
也可以说clk是clk_core的实例 */
struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
const char *con_id)
{
struct clk *clk;
/* This is to allow this function to be chained to others */
if (IS_ERR_OR_NULL(hw))
return ERR_CAST(hw);
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk)
return ERR_PTR(-ENOMEM);
clk->core = hw->core;
clk->dev_id = dev_id;
clk->con_id = con_id;
clk->max_rate = ULONG_MAX;
clk_prepare_lock();
hlist_add_head(&clk->clks_node, &hw->core->clks);
clk_prepare_unlock();
return clk;
}