目录
1. of_clk_add_provider
2. of_clk_get_from_provider
2.1 __of_clk_get_hw_from_provider
2.2 __clk_create_clk
3. of_clk_set_defaults
3.1 __set_clk_parents
3.2 __set_clk_rates
再回到第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;elsereturn 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;elsereturn 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时钟频率。