目录

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;
			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 调用关系

Logo

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

更多推荐