Linux内核4.14版本——ccf时钟子系统(6)——DTS相关的API
目录
2.1 __of_clk_get_hw_from_provider
再回到第2章DTS相关的介绍,clock driver使用一个DTS node描述一个clock provider,而clock consumer则会使用类似“clocks = <&clock 32>, <&clock 45>;”的形式引用,clock framework会自行把这些抽象的数字转换成实际的struct clk结构,怎么做的呢?肯定离不开clock provider的帮助。
1. of_clk_add_provider
第4章描述的regitser接口,负责把clocks抽象为一个一个的struct clock,与此同时,clock provider需要把这些struct clk结构保存起来,并调用clock framework的接口,将这些对应信息告知framework的OF模块,这样才可以帮助将clock consumer的DTS描述转换为struct clk结构。该接口如下:
/**
* of_clk_add_provider() - Register a clock provider for a node
* @np: Device node pointer associated with clock provider
* @clk_src_get: callback for decoding clock
* @data: context pointer for @clk_src_get callback.
*/
int of_clk_add_provider(struct device_node *np,
struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
void *data),
void *data)
{
struct of_clk_provider *cp;
int ret;
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
cp->node = of_node_get(np);
cp->data = data;
cp->get = clk_src_get;
mutex_lock(&of_clk_mutex);
list_add(&cp->link, &of_clk_providers);
mutex_unlock(&of_clk_mutex);
pr_debug("Added clock from %pOF\n", np);
ret = of_clk_set_defaults(np, true);
if (ret < 0)
of_clk_del_provider(np);
return ret;
}
np,device_node指针,clock provider在和自己的DTS匹配时获得;
clk_src_get,获取struct clk指针的回调函数,由clock provider根据实际的逻辑实现,参数说明如下:
args,struct of_phandle_args类型的指针,由DTS在解析参数时传递。例如上面的“clocks = <&clock 32>, <&clock 45>;”,32、45就是通过这个指针传进来的;
data,保存struct clk结构的指针,通常是一个数组,具体由provider决定。
data,和回调函数中的data意义相同,只是这里由provider提供,get时由clock framework core传递给回调函数。
把该provider加到全局链表of_clk_providers保存起来,方面后面其他模块使用。
对于常用的one cell clock provider,clock framework core提供一个默认的会调用函数,如下:
struct clk_onecell_data {
struct clk **clks;
unsigned int clk_num;
};
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data);
其中data指针为struct clk_onecell_data结构,该结构提供了clk指针和clk_num的对应,clock provider在regitser clocks时,同时维护一个clk和num对应的数组,并调用of_clk_add_provider接口告知clock framework core即可。
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
{
struct clk_onecell_data *clk_data = data;
unsigned int idx = clkspec->args[0];
if (idx >= clk_data->clk_num) {
pr_err("%s: invalid clock index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return clk_data->clks[idx];
}
2. of_clk_get_from_provider
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;
if (!clkspec)
return ERR_PTR(-EINVAL);
/* Check if we have such a provider in our array */
mutex_lock(&of_clk_mutex);
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)) {
if (!__clk_get(clk)) {
__clk_free_clk(clk);
clk = ERR_PTR(-ENOENT);
}
break;
}
}
mutex_unlock(&of_clk_mutex);
return clk;
}
/**
* of_clk_get_from_provider() - Lookup a clock from a clock provider
* @clkspec: pointer to a clock specifier data structure
*
* This function looks up a struct clk from the registered list of clock
* providers, an input is a clock specifier data structure as returned
* from the of_parse_phandle_with_args() function call.
*/
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
return __of_clk_get_from_provider(clkspec, NULL, __func__);
}
最终调到__of_clk_get_hw_from_provider和__clk_create_clk函数。
2.1 __of_clk_get_hw_from_provider
static struct clk_hw *
__of_clk_get_hw_from_provider(struct of_clk_provider *provider,
struct of_phandle_args *clkspec)
{
struct clk *clk;
if (provider->get_hw)
return provider->get_hw(clkspec, provider->data);
//实际调用的是它
clk = provider->get(clkspec, provider->data);
if (IS_ERR(clk))
return ERR_CAST(clk);
return __clk_get_hw(clk);
}
最终调用的是provider->get(clkspec, provider->data);这个函数赋值实际上是在of_clk_add_provider函数中,即clk_src_get,也就是默认提供的of_clk_src_onecell_get。注意of_clk_src_onecell_get函数返回的是一个struct clk结构体,但是最终__of_clk_get_hw_from_provider返回的是strutc clk_hw结构体。
2.2 __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 (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 = kstrdup_const(con_id, GFP_KERNEL);
clk->max_rate = ULONG_MAX;
clk_prepare_lock();
hlist_add_head(&clk->clks_node, &hw->core->clks);
clk_prepare_unlock();
return clk;
}
重新生成一个struct clk结构体。
3. of_clk_set_defaults
int of_clk_set_defaults(struct device_node *node, bool clk_supplier)
{
int rc;
if (!node)
return 0;
rc = __set_clk_parents(node, clk_supplier);
if (rc < 0)
return rc;
return __set_clk_rates(node, clk_supplier);
}
of_clk_set_defaults是解析dts里面时钟的配置的,一般是在上电启动过程中,根据DTS来进行时钟的初始化操作。
[ 0.000000] dump_backtrace+0x0/0x180
[ 0.000000] show_stack+0x28/0x34
[ 0.000000] dump_stack+0xa0/0xc4
[ 0.000000] of_clk_set_defaults+0x5c/0x430
[ 0.000000] of_clk_add_provider+0xa8/0x10c
[ 0.000000] _of_fixed_clk_setup+0xc0/0xf4
[ 0.000000] of_fixed_clk_setup+0x24/0x30
[ 0.000000] of_clk_init+0x1b8/0x238
[ 0.000000] time_init+0x1c/0x4c
[ 0.000000] start_kernel+0x264/0x384
of_clk_set_defaults也会在设备驱动匹配上时候调用。
[ 22.023909] Call trace:
[ 22.026378] dump_backtrace+0x0/0x180
[ 22.030097] show_stack+0x28/0x34
[ 22.033440] dump_stack+0xa0/0xc4
[ 22.036785] of_clk_set_defaults+0x5c/0x430
[ 22.041009] platform_drv_probe+0x30/0xcc
[ 22.045042] driver_probe_device+0x264/0x454
[ 22.049326] __driver_attach+0x118/0x130
[ 22.053298] bus_for_each_dev+0x7c/0xc0
[ 22.057142] driver_attach+0x34/0x40
[ 22.060734] bus_add_driver+0x1d4/0x27c
[ 22.064578] driver_register+0x80/0x120
[ 22.068422] __platform_driver_register+0x58/0x6c
[ 22.073177] cve_module_init+0x18/0x1000 [ts_cve]
[ 22.077903] do_one_initcall+0x48/0x130
[ 22.081749] do_init_module+0x64/0x28c
[ 22.085531] load_module+0x2270/0x2834
[ 22.089312] SyS_finit_module+0xc8/0xfc
[ 22.093156] el0_svc_naked+0x34/0x38
of_clk_set_defaults函数用来解析dts中assigned-{clocks/clock-parents/clock-rates}参数的,我们以下面的例子分析一下。
&spi0 {
clocks = <&ts_clk TS_CLK_TX5112_SPI_SSICLK>, <&ts_clk TS_CLK_TX5112_SPI_PCLK>;
clock-names = "ref_clk", "pclk";
assigned-clocks = <&ts_clk TS_CLK_TX5112_SPI_SSICLK>;
assigned-clock-rates = <62000000>;
assigned-clock-parents = <&ts_clk TS_CLK_TX5112_PLL1>;
};
3.1 __set_clk_parents
static int __set_clk_parents(struct device_node *node, bool clk_supplier)
{
struct of_phandle_args clkspec;
int index, rc, num_parents;
struct clk *clk, *pclk;
num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells");
if (num_parents == -EINVAL)
pr_err("clk: invalid value of clock-parents property at %pOF\n",
node);
pr_debug("num_parents: %d\n", num_parents);
for (index = 0; index < num_parents; index++) {
rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells", index, &clkspec);
if (rc < 0) {
/* skip empty (null) phandles */
if (rc == -ENOENT)
continue;
else
return rc;
}
if (clkspec.np == node && !clk_supplier)
return 0;
pclk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(pclk)) {
if (PTR_ERR(pclk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get parent clock %d for %pOF\n",
index, node);
return PTR_ERR(pclk);
}
rc = of_parse_phandle_with_args(node, "assigned-clocks",
"#clock-cells", index, &clkspec);
if (rc < 0)
goto err;
if (clkspec.np == node && !clk_supplier) {
rc = 0;
goto err;
}
clk = of_clk_get_from_provider(&clkspec);
pr_debug("%s node_name:%s clk_name:%s pclk_name:%s\n", __func__, node->name, __clk_get_name(clk), __clk_get_name(pclk));
if (IS_ERR(clk)) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get assigned clock %d for %pOF\n",
index, node);
rc = PTR_ERR(clk);
goto err;
}
rc = clk_set_parent(clk, pclk);
if (rc < 0)
pr_err("clk: failed to reparent %s to %s: %d\n",
__clk_get_name(clk), __clk_get_name(pclk), rc);
clk_put(clk);
clk_put(pclk);
}
return 0;
err:
clk_put(pclk);
return rc;
}
(1)先用of_count_phandle_with_args计算该dts中parent的个数。
(2)根据parent的个数,对每个parent进行如下操作。
a) 得到assigned-clock-parents该父时钟相关的参数结构体clkspec
b) 通过of_clk_get_from_provider得到该父时钟的struct clk结构体。
c)得到assigned-clock指定的时钟相关的参数结构体clkspec
d)通过of_clk_get_from_provider得到该时钟的struct clk结构体。
e)设置clk_set_parent子父时钟关系。
3.2 __set_clk_rates
static int __set_clk_rates(struct device_node *node, bool clk_supplier)
{
struct of_phandle_args clkspec;
struct property *prop;
const __be32 *cur;
int rc, index = 0;
struct clk *clk;
u32 rate;
of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) {
if (rate) {
rc = of_parse_phandle_with_args(node, "assigned-clocks",
"#clock-cells", index, &clkspec);
if (rc < 0) {
/* skip empty (null) phandles */
if (rc == -ENOENT)
continue;
else
return rc;
}
if (clkspec.np == node && !clk_supplier)
return 0;
clk = of_clk_get_from_provider(&clkspec);
pr_debug("%s node_name:%s clk_name:%s rate:%u\n", __func__, node->name, __clk_get_name(clk), rate);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get clock %d for %pOF\n",
index, node);
return PTR_ERR(clk);
}
rc = clk_set_rate(clk, rate);
if (rc < 0)
pr_err("clk: couldn't set %s clk rate to %u (%d), current rate: %lu\n",
__clk_get_name(clk), rate, rc,
clk_get_rate(clk));
clk_put(clk);
}
index++;
}
return 0;
}
(1)遍历assigned-clock-rates里面的结构体变量,相当于for循环。
(2)对每个rate进行如下操作。
a)得到assigned-clock指定的时钟相关的参数结构体clkspec
b)通过of_clk_get_from_provider得到该时钟的struct clk结构体。
c)设置clk_set_rate时钟频率。
3.3 调用关系

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)