Linux clock driver(2) clk_register 詳解
阿新 • • 發佈:2019-02-09
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 initialized\n",
__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_rate\n",
__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_parent\n",
__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_rate\n",
__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_names\n",
__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;
}