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