Linux CLK驱动
数据结构
copy的(https://blog.csdn.net/melody157398/article/details/122206597)
Controller
__clk_register
#define clk_hw_register_fixed_rate(dev, name, parent_name, flags, fixed_rate) \
__clk_hw_register_fixed_rate((dev), NULL, (name), (parent_name), NULL, \
NULL, (flags), (fixed_rate), 0, 0)
struct clk_hw *__clk_hw_register_fixed_rate(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
unsigned long fixed_rate, unsigned long fixed_accuracy,
unsigned long clk_fixed_flags)
{
struct clk_fixed_rate *fixed;
struct clk_hw *hw;
struct clk_init_data init = {};
int ret = -EINVAL;
/* allocate fixed-rate clock */
fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
if (!fixed)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &clk_fixed_rate_ops;
init.flags = flags;
init.parent_names = parent_name ? &parent_name : NULL;
init.parent_hws = parent_hw ? &parent_hw : NULL;
init.parent_data = parent_data;
if (parent_name || parent_hw || parent_data)
init.num_parents = 1;
else
init.num_parents = 0;
/* struct clk_fixed_rate assignments */
fixed->flags = clk_fixed_flags;
fixed->fixed_rate = fixed_rate;
fixed->fixed_accuracy = fixed_accuracy;
fixed->hw.init = &init;
/* register the clock */
hw = &fixed->hw;
if (dev || !np)
ret = clk_hw_register(dev, hw);
else if (np)
ret = of_clk_hw_register(np, hw);
if (ret) {
kfree(fixed);
hw = ERR_PTR(ret);
}
return hw;
}
传入的device_node、parent_name为空,只有name和rate,初始化clk_hw,未传入device_node,最终还是调用的clk_hw_register,其中ops为通用的clk_fixed_rate_ops,只有recalc_rate函数的实现。
int devm_clk_hw_register(struct device *dev, struct clk_hw *hw)
{
...
ret = clk_hw_register(dev, hw);
...
}
int clk_hw_register(struct device *dev, struct clk_hw *hw)
{
return PTR_ERR_OR_ZERO(__clk_register(dev, dev_or_parent_of_node(dev),
hw));
}
static struct clk *
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
{
...
hw->init = NULL;
core->name = kstrdup_const(init->name, GFP_KERNEL);
core->ops = init->ops;
ret = clk_core_populate_parent_map(core, init);
INIT_HLIST_HEAD(&core->clks);
clk_core_link_consumer(core, hw->clk);
__clk_core_init
...
}
static int __clk_core_init(struct clk_core *core)
{
...
if (clk_core_lookup(core->name)){
}
if (core->ops->init) {
ret = core->ops->init(core->hw);
}
parent = core->parent = __clk_init_parent(core);
{
if (core->num_parents > 1 && core->ops->get_parent)
index = core->ops->get_parent(core->hw);
return clk_core_get_parent_by_index(core, index);
-->clk_core_fill_parent_index(core, index);
return core->parents[index].core;
}
if (parent) {
hlist_add_head(&core->child_node, &parent->children);
core->orphan = parent->orphan;
} else if (!core->num_parents) {
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;
}
if (core->ops->recalc_accuracy)
core->accuracy = core->ops->recalc_accuracy(core->hw,
clk_core_get_accuracy_no_lock(parent));
else if (parent)
core->accuracy = parent->accuracy;
else
core->accuracy = 0;
clk_core_reparent_orphans_nolock
...
}
clk_register首先初始化clk_ops,clk_core_populate_parent_map初这个函数主要是根据parent_num申请了clk_parent_map,则将parent_names[i]字符串赋值给了clk_parent_map的name。
初始化core->clks链表,然后clk_core_link_consumer将hw->clk->clk_node作为core->clks链表的头
__clk_core_init先检查是否有相同的同名函数已经注册,然后检查一些函数接口的完整性,有init函数先执行init函数,然后__clk_init_parent得到parent,这里执行get_parent来读取寄存器得到index,如果parent已经被初始化过了,则将其插入到parent->children链表,没有初始化且没有父时钟插入到root链表,否则插入到orphan链表。
有recalc_accuracy函数则执行该函数计算时钟准确率,在默写形况下可能对选择父时钟有用。然后执行recalc_rate获取时钟速率
clk的parent是在__clk_init_parent的时候被填充的,没有全部填充,先执行get_parent获取index,然后执行clk_core_fill_parent_index填充当前的parent
某些时钟最好需要有CLK_IS_CRITICAL的flag,这些关键时钟表示需要一直是工作的,例如cpu的时钟登,有该标志位会执行clk_core_prepare和clk_core_enable,不会被后late_initcall调用clk_disable_unused给关掉
clk_core_reparent_orphans_nolock函数遍历orphan链表(没看明白)
of_clk_add_hw_provider
devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_clks);
...
int devm_of_clk_add_hw_provider(struct device *dev,
struct clk_hw *(*get)(struct of_phandle_args *clkspec,
void *data),
void *data)
{
...
np = get_clk_provider_node(dev);
ret = of_clk_add_hw_provider(np, get, data);
...
}
int of_clk_add_hw_provider(struct device_node *np,
struct clk_hw *(*get)(struct of_phandle_args *clkspec,
void *data),
void *data)
{
...
struct of_clk_provider *cp;
cp->node = of_node_get(np);
cp->data = data;
cp->get_hw = get;
list_add(&cp->link, &of_clk_providers);
clk_core_reparent_orphans();
...
}
get函数和void *data为回调函数,会在consumer执行get_clk的时候调用
当前device如果没有"#clock-cells"属性则寻找父节点,获取device_node
创建一个clk_provider,设置他的data为传入的hw_clks,get_hw为of_clk_hw_onecell_get函数,将其加入到clk_provider链表。
Consumer
devm_clk_get
struct clk *devm_clk_get(struct device *dev, const char *id)
{
...
clk = clk_get(dev, id);
...
}
struct clk *clk_get(struct device *dev, const char *con_id)
-->of_clk_get_hw(dev->of_node, 0, con_id);
{
ret = of_parse_clkspec(np, index, con_id, &clkspec);
hw = of_clk_get_hw_from_clkspec(&clkspec);
-->hw = __of_clk_get_hw_from_provider(provider, clkspec);
-->return provider->get_hw(clkspec, provider->data);
}
devm_clk_get通过传入的id找到clock-names指向的父节点,在provider链表中查找和父节点相同的node,然后执行get_hw,get_hw为provider注册时指定的of_clk_hw_onecell_get
struct clk_hw *
of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data)
{
struct clk_hw_onecell_data *hw_data = data;
unsigned int idx = clkspec->args[0];
if (idx >= hw_data->num) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return hw_data->hws[idx];
}
clk_hw_onecell_data在controller初始化
clk_prepare_enable
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_prepare(clk)
-->clk_core_prepare_lock
-->clk_core_prepare
{
if (core->prepare_count == 0)
{
ret = clk_core_prepare(core->parent);
if (core->ops->prepare)
ret = core->ops->prepare(core->hw);
}
core->prepare_count++;
}
clk_enable
-->clk_core_enable_lock
-->clk_core_enable
{
if (core->enable_count == 0)
{
ret = clk_core_enable(core->parent);
if (core->ops->enable)
ret = core->ops->enable(core->hw);
}
core->enable_count++;
}
clk_prepare、clk_enable的逻辑差不多,core->enable_count==0的时候,先要递归父节点都执行enable,然后在core->enable_count++,也就是说存在一个子节点enable则父节点count++,子节点disable的时候父节点count–,如果为0了则代表子节点全部disable,父节点也执行disable。
clk_disable_unprepare
clk_disable_unprepare逻辑和clk_prepare_enable相似
clk_get_rate
clk_get_rate
-->clk_core_get_rate_recalc
{
if (core && (core->flags & CLK_GET_RATE_NOCACHE))
__clk_recalc_rates(core, 0);
return clk_core_get_rate_nolock(core);
}
如果没有CLK_GET_RATE_NOCACHE的flag则执行core->ops->recalc_rate(core->hw, parent_rate)重新获取rate
clk_set_rate
clk_set_rate
-->clk_core_set_rate_nolock
-->clk_core_req_round_rate_nolock
-->clk_core_round_rate_nolock
-->clk_core_determine_round_nolock
-->core->ops->determine_rate(core->hw, req);
-->core->ops->round_rate(core->hw, req->rate,&req->best_parent_rate)
return req.rate
-->有determine_rate优先执行,没有则执行round_rate
-->clk_calc_new_rates
-->round和determine接口有点不同,determine可能会改变parent
-->clk_core_determine_round_nolock(core, &req);
-->如果执行了determine_rate,req除了rate外还有best_parent
-->clk_calc_subtree(core,new_rate,parent,p_index)
-->根据前面的代码,parent可能为null,或者为best_parent
-->hlist_for_each_entry(child, &core->children, child_node)
clk_recalc
-->core->ops->recalc_rate(core->hw, parent_rate)
-->clk_change_rate
-->看parent是否改变,改变执行set_rate_and_parent
-->core->ops->set_parent(core->hw, core->new_parent_index)
-->core->ops->set_rate
-->hlist_for_each_entry_safe(child, tmp, &core->children, child_node)
clk_change_rate 递归执行set_rate
clk_set_rate先执行round_rate,然后重新计算子节点的时钟,然后当前节点和所有子节点执行set_rate,其中要注意parent是否发送改变。以上是没有determine_rate只有round_rate的情况。round_rate同过标志位选择是否改变父时钟频率,而determine_rate是在多个父时钟的情况下选择最接近的父时钟
__clk_mux_determine_rate
const struct clk_ops mt_mux_ops{
...
.determine_rate = __clk_mux_determine_rate,
...
};
__clk_mux_determine_rate
-->clk_mux_determine_rate_flags(hw, req, 0)
-->for (i = 0; i < num_parents; i++){
parent = clk_core_get_parent_by_index(core, i);
parent_req.rate = clk_core_get_rate_nolock(parent);
}
-->遍历parent,得到req->best_parent_hw和req->best_parent_rate
return 0 success
determine_rate这个接口要注意,和parent有关,会在如下clk_round_rate、clk_core_req_round_rate_nolock中被调用。clk_core_get_parent_by_index如何index的parent为空会从clk树中检索填充,获取该parent的rate,
determine_rate这个接口要注意,有这个接口在执行set_rate的过程中可能会更改父时钟
clk_set_parent
clk_set_parent
-->clk_core_set_parent_nolock
-->p_index = clk_fetch_parent_index(core, parent);
-->for(i = 0; i < core->num_parents; i++)
{
if (core->parents[i].core == parent)
return i;
if (core->parents[i].core)
continue;
if (core->parents[i].hw) {
if (core->parents[i].hw == parent->hw)
break;
continue;
}
if (parent == clk_core_get(core, i))
break;
if (core->parents[i].name &&
!strcmp(parent->name, core->parents[i].name))
break;
}
return i;
-->p_rate = parent->rate;
-->__clk_set_parent
-->old_parent = __clk_set_parent_before(core, parent);
-->core->ops->set_parent(core->hw, p_index);
-->__clk_recalc_rates(core, ABORT_RATE_CHANGE);
-->core->rate = clk_recalc(core, parent_rate);
-->core->ops->recalc_rate(core->hw, parent_rate);
-->hlist_for_each_entry(child, &core->children, child_node)
__clk_recalc_rates(child, msg);
clk_fetch_parent_index中遍历所有parent,如果core->parents[i].core为空,则执行clk_core_get来获取parent的clk_core,否则通过name来,找得到返回index。然后执行set_parent,最后遍历子节点执行recalc_rate,奇怪了?这个set_parent之后会重新recalc_rate,但是不会执行set_rate,不合理啊
clk_ignore_unused
static bool clk_ignore_unused __initdata;
static int __init clk_ignore_unused_setup(char *__unused)
{
clk_ignore_unused = true;
return 1;
}
__setup("clk_ignore_unused", clk_ignore_unused_setup);
bootargs中需要有clk_ignore_unused参数,会执行clk_ignore_unused_setup将clk_ignore_unused置为true,否则会执行clk_disable_unused
static int __init clk_disable_unused(void)
{
struct clk_core *core;
if (clk_ignore_unused) {
pr_warn("clk: Not disabling unused clocks\n");
return 0;
}
pr_warn("********************************************************************\n");
pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
pr_warn("** If your kernel hangs after printing this message, it may be that clk_ignore_unused is not set in the bootargs attribute of uboot **\n");
pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
pr_warn("********************************************************************\n");
clk_prepare_lock();
hlist_for_each_entry(core, &clk_root_list, child_node)
clk_disable_unused_subtree(core);
hlist_for_each_entry(core, &clk_orphan_list, child_node)
clk_disable_unused_subtree(core);
hlist_for_each_entry(core, &clk_root_list, child_node)
clk_unprepare_unused_subtree(core);
hlist_for_each_entry(core, &clk_orphan_list, child_node)
clk_unprepare_unused_subtree(core);
clk_prepare_unlock();
return 0;
}
  late_initcall_sync(clk_disable_unused);
late_initcall会调用clk_disable_unused,没设置clk_ignore_unused为true的话会将
clk_set_rate_exclusive
设置exclusive_count++ 独占访问
CLK_OF_DECLARE
就是个宏,将函数放在指定section中,在很早的时候就调用了,函数内还是需要调用clk_register来册时钟
start_kernel
-->time_init
-->of_clk_init