of_parse_phandle_with_args 函数

Linux version: 4.14

Code link: Linux source code (v4.14) - Bootlin


1  phandle 属性及相关的结构体

(1)phandle 属性

       在内核中 phandle 是无符号整型变量:

typedef u32 phandle;

        phandle 属性为 devicetree 中唯一的节点指定一个数字标识符,节点中的 phandle 属性,它的取值必须是唯一的(不能和其他的phandle值一样),例如:

pic@10000000 {
    phandle = <1>;
    interrupt-controller;
};
another-device-node {
    interrupt-parent = <1>;   // 使用phandle值为1来引用上述节点
};

// 注:DTS中的大多数设备树将不包含显式的phandle属性,当DTS被编译成二进制DTB格式时,
//     DTC工具会自动插入phandle属性。

        我们还可以给一个设备节点添加label,之后可以通过&label的形式访问这个label,这种引用是通过 phandle(pointer handle)进行的。例如,在arch/arm/boot/dts/omap5.dtsi中,第3组GPIO有gpio3这个label:

gpio3: gpio@48057000 {
        compatible = "ti,omap4-gpio";
        reg = <0x48057000 0x200>;
        interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
        ti,hwmods = "gpio3";
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
};

        而hsusb2_phy这个USB的PHY复位GPIO用的是这组GPIO中的一个,所以它通过phandle引用了“gpio3”。hsusb2_phy通过&gpio3引用了这个节点,表明自己要使用这一组GPIO中的第12个GPIO。很显然,这种phandle引用其实表明硬件之间的一种关联性。

/* HS USB Host PHY on PORT 2 */
hsusb2_phy: hsusb2_phy {
         compatible = "usb-nop-xceiv";
         reset-gpios = <&gpio3 12 GPIO_ACTIVE_LOW>; /* gpio3_76 HUB_RESET */
};

(2)of_phandle_iterator 结构体

该结构体用于解析 phandle 参数 

struct of_phandle_iterator {
	/* Common iterator information */
	const char *cells_name;               // cells name
	int cell_count;                       
	const struct device_node *parent;     // list 所在的结点

	/* List size information */
	const __be32 *list_end;               // list 参数的结束位置
	const __be32 *phandle_end;            // 当前 phandle 参数的结束位置

	/* Current position state */
	const __be32 *cur;                    // 指向当前的 list 参数
	uint32_t cur_count;                   // 当前 phandle 参数的个数
	phandle phandle;                      // 当前 phandle 的 id
	struct device_node *node;             // 当前 phandle 所在的节点
};

 (3)of_phandle_args 结构体

该结构体用于保存解析出的 phandle 参数 

#define MAX_PHANDLE_ARGS 16           // phandle 的最大参数个数为 16

struct of_phandle_args {
	struct device_node *np;           // phandle 所在的节点
	int args_count;                   // phandle 参数的个数
	uint32_t args[MAX_PHANDLE_ARGS];  // phandle 的参数内容
};

2  函数 of_parse_phandle_with_args

① np: 指向包含列表的设备树节点的指针

② list_name:包含列表的属性名称

③ cells_name:指定phandles参数计数的属性名称

④ index:要解析的phandle的索引

⑤ out_args:指向输出参数结构的可选指针(将被填充)

int of_parse_phandle_with_args(const struct device_node *np, const char *list_name,
				const char *cells_name, int index,
				struct of_phandle_args *out_args)
{
	if (index < 0)
		return -EINVAL;
	return __of_parse_phandle_with_args(np, list_name, cells_name, 0,
					    index, out_args);
}
EXPORT_SYMBOL(of_parse_phandle_with_args);

3  函数 __of_parse_phandle_with_args

static int __of_parse_phandle_with_args(const struct device_node *np,
					const char *list_name,
					const char *cells_name,
					int cell_count, int index,
					struct of_phandle_args *out_args)
{
	struct of_phandle_iterator it;
	int rc, cur_index = 0;

	/* Loop over the phandles until all the requested entry is found */
	of_for_each_phandle(&it, rc, np, list_name, cells_name, cell_count) {
		/*
		 * All of the error cases bail out of the loop, so at
		 * this point, the parsing is successful. If the requested
		 * index matches, then fill the out_args structure and return,
		 * or return -ENOENT for an empty entry.
		 */
		rc = -ENOENT;
		if (cur_index == index) {
			if (!it.phandle)
				goto err;

			if (out_args) {
				int c;

				c = of_phandle_iterator_args(&it,
							     out_args->args,
							     MAX_PHANDLE_ARGS);
				out_args->np = it.node;
				out_args->args_count = c;
			} else {
				of_node_put(it.node);
			}

			/* Found it! return success */
			return 0;
		}

		cur_index++;
	}

	/*
	 * Unlock node before returning result; will be one of:
	 * -ENOENT : index is for empty phandle
	 * -EINVAL : parsing error on data
	 */

 err:
	of_node_put(it.node);
	return rc;
}

该函数遍历 list_name 属性中的每一个 phandle ,并将 index 指定的那个 phandle 的参数解析到 out_args 中。注意,index = 0 表示第一个 phandle,以此类推,该函数执行成功时返回 0 。

4  函数 of_for_each_phandle

该函数将phandle相关的值解析到 it 结构体中

#define of_for_each_phandle(it, err, np, ln, cn, cc)			\
	for (of_phandle_iterator_init((it), (np), (ln), (cn), (cc)),	\
	     err = of_phandle_iterator_next(it);			\
	     err == 0;							\
	     err = of_phandle_iterator_next(it))

该函数只是执行一个for循环

① 初始条件 

of_phandle_iterator_init((it), (np), (ln), (cn), (cc))
err = of_phandle_iterator_next(it)

② 判断条件,若 err==0 则继续循环,否则退出循环

err == 0

③ 步进执行的语句

err = of_phandle_iterator_next(it)

(1)函数 of_phandle_iterator_init

int of_phandle_iterator_init(struct of_phandle_iterator *it,
		const struct device_node *np,
		const char *list_name,
		const char *cells_name,
		int cell_count)
{
	const __be32 *list;
	int size;

	memset(it, 0, sizeof(*it));

    // 读取np结点中的"gpio"或"gpios"的属性值,将读取到的值保存在list指向的空间中
    // 读取到的长度保存在size指向的变量中(字节长度)
	list = of_get_property(np, list_name, &size);
	if (!list)
		return -ENOENT;

	it->cells_name = cells_name;
	it->cell_count = cell_count;
	it->parent = np;
	it->list_end = list + size / sizeof(*list);
	it->phandle_end = list;
	it->cur = list;

	return 0;
}
EXPORT_SYMBOL_GPL(of_phandle_iterator_init);

首先调用函数 of_get_property() 函数获得当前节点的 "list_name" (gpio或者gpios)属性的值,将解析到的一系列的值保存到list指针指向的存储空间中,然后计算并 list_end 的值(表示list中有多少个参数)。

(2)函数 of_phandle_iterator_next

int of_phandle_iterator_next(struct of_phandle_iterator *it)
{
	uint32_t count = 0;

	if (it->node) {
		of_node_put(it->node);
		it->node = NULL;
	}

	if (!it->cur || it->phandle_end >= it->list_end)
		return -ENOENT;

	it->cur = it->phandle_end;

	/* If phandle is 0, then it is an empty entry with no arguments. */
	it->phandle = be32_to_cpup(it->cur++);

	if (it->phandle) {

		/*
		 * Find the provider node and parse the #*-cells property to
		 * determine the argument length.
		 */
		it->node = of_find_node_by_phandle(it->phandle);

		if (it->cells_name) {
			if (!it->node) {
				pr_err("%pOF: could not find phandle\n",
				       it->parent);
				goto err;
			}

            // 读取it->node结点中的"#gpio-cells"属性值,并保存在变量count变量中
			if (of_property_read_u32(it->node, it->cells_name,
						 &count)) {
				pr_err("%pOF: could not get %s for %pOF\n",
				       it->parent,
				       it->cells_name,
				       it->node);
				goto err;
			}
		} else {
			count = it->cell_count;
		}

		/*
		 * Make sure that the arguments actually fit in the remaining
		 * property data length
		 */
		if (it->cur + count > it->list_end) {
			pr_err("%pOF: arguments longer than property\n",
			       it->parent);
			goto err;
		}
	}

	it->phandle_end = it->cur + count;
	it->cur_count = count;

	return 0;

err:
	if (it->node) {
		of_node_put(it->node);
		it->node = NULL;
	}

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(of_phandle_iterator_next);

① 其中 of_find_node_by_phandle

根据结点中phandle的值找到该节点。dts文件被编译为dtb文件时,每个节点都有一个数字ID,这些ID彼此不同。可以用数字ID来找到device_node。这些数字ID就是phandle。

struct device_node *of_find_node_by_phandle(phandle handle)
{
	struct device_node *np;
	unsigned long flags;

	if (!handle)
		return NULL;

	raw_spin_lock_irqsave(&devtree_lock, flags);
	for_each_of_allnodes(np)
		if (np->phandle == handle)
			break;
	of_node_get(np);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return np;
}
EXPORT_SYMBOL(of_find_node_by_phandle);

② 其中 for_each_of_allnodes

#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn)

该函数从根节点遍历结点树,每次遍历到的结点保存在 dn 变量中。

③ 其中 for_each_of_allnodes_from

#define for_each_of_allnodes_from(from, dn) \
	for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn))

该函数从from结点开始遍历结点树,每次遍历的结点保存在dn变量中。

④ 其中 __of_find_all_nodes 如下所示

struct device_node *__of_find_all_nodes(struct device_node *prev)
{
	struct device_node *np;
	if (!prev) {
		np = of_root;
	} else if (prev->child) {
		np = prev->child;
	} else {
		/* Walk back up looking for a sibling, or the end of the structure */
		np = prev;
		while (np->parent && !np->sibling)
			np = np->parent;
		np = np->sibling; /* Might be null at the end of the tree */
	}
	return np;
}

该函数用于遍历结点树,每次按照遍历的规则返回 prev 结点的下一个结点。注意,如果传入的 prev 的值为空,则返回根节点 of_root。

(3)补充: of_node_get 函数和 of_node_put 函数

of_node_get函数:  device_node计数增加
of_node_put函数:  device_node计数减少

5  函数 of_phandle_iterator_args

该函数用于将 it 变量中当前的 phandle 对应的参数提取到 args 中,该函数返回值为解析出的参数的个数。(注意:解析的参数不包括phandle本身)

int of_phandle_iterator_args(struct of_phandle_iterator *it,
			     uint32_t *args,
			     int size)
{
	int i, count;

	count = it->cur_count;

	if (WARN_ON(size < count))
		count = size;

	for (i = 0; i < count; i++)
		args[i] = be32_to_cpup(it->cur++);

	return count;
}

其中 be32_to_cpup 用来转换一个无符号的, 大端, 32位值到一个cpu的大小端值。
(如果cpu为大端序,就将数值直接返回此值;如果cpu为小端序,就将数值转换为小端序)

#define be32_to_cpup __be32_to_cpup


// CPU 为大端字节序
static __always_inline __u32 __be32_to_cpup(const __be32 *p)
{
	return (__force __u32)*p;
}

// CPU 为小端字节序
static __always_inline __u32 __be32_to_cpup(const __be32 *p)
{
	return __swab32p((__u32 *)p);
}

总结:

int of_parse_phandle_with_args(const struct device_node *np, const char *list_name,
				const char *cells_name, int index,
				struct of_phandle_args *out_args)

函数 of_parse_phandle_with_args 用于将结点 np 的 list_name 属性中的第 index 个 phandle 后面的参数解析到 out_args 中,其中 phandle 后面的参数个数通过解析 phandle 对应结点的 cells_name 属性来获取。

举例:

gpio1: gpio@e000a000 {
    compatible = "xlnx,zynq-gpio-1.0";
    #gpio-cells = <2>;
    clocks = <&clkc 42>;
    gpio-controller;
    interrupt-controller;
    #interrupt-cells = <2>;
    interrupt-parent = <&intc>;
    interrupts = <0 20 4>;
    reg = <0xe000a000 0x1000>;
};


led {
    label = "led";
    gpios = <&gpio0 0 1 &gpio1 2 3>;
};


ret = of_parse_phandle_with_args(led, "gpios", "#gpio-cells", 1, &out_args);

这里解析的 phandle 是 "&gpio1",若解析成功,则返回值 ret = 0 ,且解析出的结果如下:

out_args[0] = 2
out_args[1] = 3
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vane Zhang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值