参考:
Linux 时钟框架学习笔记_我的学习笔记-CSDN博客___timer_of_table
kernel - clock - qzhang1535 - 博客园
of_clk_get_by_name函数分析:
struct clk *of_clk_get_by_name(struct device_node *np, const char *name)
{
return __of_clk_get_by_name(np, np->full_name, name);
}
static struct clk *__of_clk_get_by_name(struct device_node *np,
const char *dev_id,
const char *name)
{
struct clk *clk = ERR_PTR(-ENOENT);
/* Walk up the tree of devices looking for a clock that matches */
while (np) {
int index = 0;
/*
* For named clocks, first look up the name in the "clock-names" property. If it
* cannot be found, then index will be an error code, and of_clk_get() will fail.
*/
if (name)
index = of_property_match_string(np, "clock-names", name);
clk = __of_clk_get(np, index, dev_id, name);
if (!IS_ERR(clk)) {
break;
}
}
return clk;
}
static struct clk *__of_clk_get(struct device_node *np, int index,
const char *dev_id, const char *con_id)
{
struct of_phandle_args clkspec;
struct clk *clk;
int rc;
rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,&clkspec);
clk = __of_clk_get_from_provider(&clkspec, dev_id, con_id);
of_node_put(clkspec.np);
return clk;
}
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
const char *dev_id, const char *con_id)
{
struct of_clk_provider *provider;
struct clk *clk = ERR_PTR(-EPROBE_DEFER);
struct clk_hw *hw;
/* Check if we have such a provider in our array */
list_for_each_entry(provider, &of_clk_providers, link) {
if (provider->node == clkspec->np) {
hw = __of_clk_get_hw_from_provider(provider, clkspec);
clk = __clk_create_clk(hw, dev_id, con_id);
}
if (!IS_ERR(clk)) {
break;
}
}
return clk;
}
static struct clk_hw * __of_clk_get_hw_from_provider(struct of_clk_provider *provider,
struct of_phandle_args *clkspec)
{
struct clk *clk;
clk = provider->get(clkspec, provider->data);
return __clk_get_hw(clk);
}
clk_prepare_enable函数分析:
static inline int clk_prepare_enable(struct clk *clk)
{
int ret;
ret = clk_prepare(clk);
ret = clk_enable(clk);
return ret;
}
clk_prepare函数:
int clk_prepare(struct clk *clk)
{
return clk_core_prepare_lock(clk->core);
}
static int clk_core_prepare_lock(struct clk_core *core)
{
int ret;
ret = clk_core_prepare(core);
return ret;
}
static int clk_core_prepare(struct clk_core *core)
{
int ret = 0;
if (!core)
return 0;
if (core->prepare_count == 0) {
ret = clk_core_prepare(core->parent); //递归prepare父时钟,父时钟如果还有父时钟,逐个往上追溯
if (ret)
return ret;
if (core->ops->prepare)
ret = core->ops->prepare(core->hw); //prepare自己
if (ret) {
clk_core_unprepare(core->parent);
return ret;
}
}
core->prepare_count++;
return 0;
}
clk_enable函数分析:
int clk_enable(struct clk *clk)
{
return clk_core_enable_lock(clk->core);
}
static int clk_core_enable_lock(struct clk_core *core)
{
unsigned long flags;
int ret;
ret = clk_core_enable(core);
return ret;
}
static int clk_core_enable(struct clk_core *core)
{
int ret = 0;
if (!core)
return 0;
if (core->enable_count == 0) {
ret = clk_core_enable(core->parent); //递归使能父时钟,父时钟如果还有父时钟,逐个往上追溯
if (ret)
return ret;
if (core->ops->enable)
ret = core->ops->enable(core->hw); //最后使能自己的时钟
if (ret) {
clk_core_disable(core->parent);
return ret;
}
}
core->enable_count++;
return 0;
}
总结:
1、clk_prepare_enable函数最终分别调用了core->ops->prepare( )、core->ops->enable( ),core->ops 是在 clk_register 函数里初始化的。
2、内核使用 Common Clk Framework (CCF)框架,其作用是构建了一个时钟树结构,根时钟下有子时钟,子时钟可以再有子时钟。驱动使用统一的接口去使能一个时钟时,会先递归使能父时钟,然后再使能自己,最终效果是从根时钟开始使能直到叶子时钟使能结束。
补充:
时钟树:参考dts中clk节点布局可知,其完全就是一棵树。