我是靠谱客的博主 苹果裙子,最近开发中收集的这篇文章主要介绍Linux clock driver(2) clk_register 详解clk_register 详解,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
clk_register 详解
clk_register是底层clock driver用来向CCF(common clock framework)层注册时钟节点的接口,是CCF中的关键函数之一,下面将结合程序详细介绍其功能。
先看一下来看一下 clk_register 函数的实现:
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
int i, ret;
struct clk_core *core;
core = kzalloc(sizeof(*core), GFP_KERNEL);
if (!core) {
ret = -ENOMEM;
goto fail_out;
}
core->name = kstrdup_const(hw->init->name, GFP_KERNEL);
if (!core->name) {
ret = -ENOMEM;
goto fail_name;
}
core->ops = hw->init->ops;
if (dev && dev->driver)
core->owner = dev->driver->owner;
core->hw = hw;
core->flags = hw->init->flags;
core->num_parents = hw->init->num_parents;
core->min_rate = 0;
core->max_rate = ULONG_MAX;
hw->core = core;
/* allocate local copy in case parent_names is __initdata */
core->parent_names = kcalloc(core->num_parents, sizeof(char *),
GFP_KERNEL);
if (!core->parent_names) {
ret = -ENOMEM;
goto fail_parent_names;
}
/* copy each string name in case parent_names is __initdata */
for (i = 0; i < core->num_parents; i++) {
core->parent_names[i] = kstrdup_const(hw->init->parent_names[i],
GFP_KERNEL);
if (!core->parent_names[i]) {
ret = -ENOMEM;
goto fail_parent_names_copy;
}
}
/*创建哈希表头,每次调用get_clk接口都会创建一个clk实例,并将其加入此哈希表中,
注:每个clock由一个struct clk_core描述,其与struct clk_hw是一一对应的关系,
但是struct clk可能有很多个,其他驱动需要操作clock时,都需要先分配一个
struct clk 类型的指针,因此其与struct clk_core是一对多的关系,
也可以说clk是clk_core的实例*/
INIT_HLIST_HEAD(&core->clks);
hw->clk = __clk_create_clk(hw, NULL, NULL);
if (IS_ERR(hw->clk)) {
ret = PTR_ERR(hw->clk);
goto fail_parent_names_copy;
}
ret = __clk_init(dev, hw->clk);
if (!ret)
return hw->clk;
__clk_free_clk(hw->clk);
hw->clk = NULL;
fail_parent_names_copy:
while (--i >= 0)
kfree_const(core->parent_names[i]);
kfree(core->parent_names);
fail_parent_names:
kfree_const(core->name);
fail_name:
kfree(core);
fail_out:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(clk_register);
此函数的主要功能可分为3点:
1. 分配struct clk_core 类型的变量,并依据传入的hw指针对其进行初始化
2. 调用 __clk_create_clk 函数分配struct clk类型的变量
3. 调用__clk_init完成clock 的初始化
__clk_create_clk 函数的实现如下:
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 (!hw || IS_ERR(hw))
return (struct clk *) hw;
clk = kzalloc(sizeof(*clk), GFP_KERNEL); //分配struct clk类型的变量
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节点加入core->clks队列中
clk_prepare_unlock();
return clk;
}
__clk_init 函数实现如下:
static int __clk_init(struct device *dev, struct clk *clk_user)
{
int i, ret = 0;
struct clk_core *orphan;
struct hlist_node *tmp2;
struct clk_core *core;
unsigned long rate;
if (!clk_user)
return -EINVAL;
core = clk_user->core;
clk_prepare_lock();
/* 判断此clk是否已经注册过了 */
if (clk_core_lookup(core->name)) {
pr_debug("%s: clk %s already initializedn",
__func__, core->name);
ret = -EEXIST;
goto out;
}
/* 判断底层clock的操作函数的设计是否符合规定. 可参考 Documentation/clk.txt */
if (core->ops->set_rate &&
!((core->ops->round_rate || core->ops->determine_rate) &&
core->ops->recalc_rate)) {
pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_raten",
__func__, core->name);
ret = -EINVAL;
goto out;
}
if (core->ops->set_parent && !core->ops->get_parent) {
pr_warning("%s: %s must implement .get_parent & .set_parentn",
__func__, core->name);
ret = -EINVAL;
goto out;
}
if (core->ops->set_rate_and_parent &&
!(core->ops->set_parent && core->ops->set_rate)) {
pr_warn("%s: %s must implement .set_parent & .set_raten",
__func__, core->name);
ret = -EINVAL;
goto out;
}
/* 如果有parent name为null,则产生WARN */
for (i = 0; i < core->num_parents; i++)
WARN(!core->parent_names[i],
"%s: invalid NULL in %s's .parent_namesn",
__func__, core->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 core->parents[]
* must always check for a NULL pointer and try to populate it if
* necessary.
*
* If core->parents is not NULL we skip this entire block. This allows
* for clock drivers to statically initialize core->parents.
*/
if (core->num_parents > 1 && !core->parents) {
core->parents = kcalloc(core->num_parents, sizeof(struct clk *),
GFP_KERNEL);
/*
* clk_core_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.
*/
if (core->parents)
for (i = 0; i < core->num_parents; i++)
core->parents[i] =
clk_core_lookup(core->parent_names[i]);
}
core->parent = __clk_init_parent(core);
/*判断此节点是否为孤儿节点,如果有parent且parent已经初始化,则其延续父节点的特性,如果为ROOT节点,
就肯定不是孤儿节点了,否则为孤儿节点*/
if (core->parent) {
hlist_add_head(&core->child_node,
&core->parent->children);
core->orphan = core->parent->orphan;
} else if (core->flags & CLK_IS_ROOT) {
hlist_add_head(&core->child_node, &clk_root_list);
core->orphan = false;
} else {
hlist_add_head(&core->child_node, &clk_orphan_list);
core->orphan = true;
}
/*此处应该是设置精度,但是从我们的底层驱动看,这个都是设置为默认值0*/
if (core->ops->recalc_accuracy)
core->accuracy = core->ops->recalc_accuracy(core->hw,
__clk_get_accuracy(core->parent));
else if (core->parent)
core->accuracy = core->parent->accuracy;
else
core->accuracy = 0;
/*设置相位,默认为0*/
if (core->ops->get_phase)
core->phase = core->ops->get_phase(core->hw);
else
core->phase = 0;
/*设置时钟频率,如果实现了recalc_rate函数,则通过此函数获取,如果有parent,则和parent频率一致,否则设置为0*/
if (core->ops->recalc_rate)
rate = core->ops->recalc_rate(core->hw,
clk_core_get_rate_nolock(core->parent));
else if (core->parent)
rate = core->parent->rate;
else
rate = 0;
core->rate = core->req_rate = rate;
/*查看当前结构是否为某些孤儿节点的parent,如果是,则reparent*/
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 (i >= 0 && i < orphan->num_parents &&
!strcmp(core->name, orphan->parent_names[i]))
clk_core_reparent(orphan, core);
continue;
}
/*如果没有实现get_parent,则遍历parents列表*/
for (i = 0; i < orphan->num_parents; i++)
if (!strcmp(core->name, orphan->parent_names[i])) {
clk_core_reparent(orphan, core);
break;
}
}
/*如无特殊硬件层面的限制,不建议实现此init函数*/
if (core->ops->init)
core->ops->init(core->hw);
kref_init(&core->ref);
out:
clk_prepare_unlock();
if (!ret)
clk_debug_register(core);
return ret;
}
最后
以上就是苹果裙子为你收集整理的Linux clock driver(2) clk_register 详解clk_register 详解的全部内容,希望文章能够帮你解决Linux clock driver(2) clk_register 详解clk_register 详解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复