本篇文章是根据Linux-4.9.88内核进行分析的(imx6ull)。
使用过pinctrl的同学们都知道pinctrl系统分成了两个部分:
所以,这篇文章首先会先介绍描述pinctrl涉及的一些数据结构、pinctrl如何将设备树与数据结构挂钩、client程序如何使用内核提供的设备树资源。
首先要明确的是,pinctrl的三大作用:
使用pinctrl_dev结构体来描述一个pincontroller,其中包含了重要的结构体pinctrl_desc,这个结构体是重点。
而在内核中,我们无需再去构造一个pinctrl_dev结构体,而是构造pinctrl_desc结构体,之后使用内核提供的函数pinctrl_register,便可以获得一个pinctrl_dev结构。
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data);
这个函数中有三个重要结构体
站在client的角度,我们知道,用户提供的设备树最终会被转换为一个platform_device或者是一个固定的结构设备,如i2c_client。
也就是说,描述设备的结构体,总会存在一个struct device结构,其中包含有struct dev_pin_info结构,来表述设备树。
这个结构主要是站在用户的角度,因为在用户编写的设备树中,会描述几个状态,来对应不同的引脚配置,比如:
假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?
这需要通过设备树来确定:
分析设备树,找到pin controller
对于每个状态,比如default、init,去分析pin controller中的设备树节点
使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一系列的pinctrl_map
这些pinctrl_map放在pinctrl.dt_maps链表中
每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中
设备引用pin controller中的某个节点时,这个节点会被转换为一些列的pinctrl_map:
转换为多少个pinctrl_map,完全由具体的驱动决定
每个pinctrl_map,又被转换为一个pinctrl_setting
举例,设备节点里有:pinctrl-0 = <&state_0_node_a>
pinctrl-0对应一个状态,会得到一个pinctrl_state
state_0_node_a节点被解析为一系列的pinctrl_map
这一系列的pinctrl_map被转换为一系列的pinctrl_setting
这些pinctrl_setting被放入pinctrl_state的settings链表
drivers\pinctrl\freescale\pinctrl-imx6ul.c中是我们程序的入口。
可以看出来imx_pinctrl_probe是一个通用的imx系统设备树的probe程序。
下面分析该函数
imx_pinctrl_probe:
根据上述的结构体介绍可以知道,构造了一个pinctrl_desc结构,以及其中的引脚操作函数结构体,最后会调用imx_pinctrl_probe_dt函数,用来解析内核提供的设备树。调用devm_pinctrl_register函数将pinctrl_desc构造pinctrl_dev结构。
imx_pinctrl_probe_dt:
将程序与设备树结合来看,其中
imx_pinctrl_parse_functions:
之后解析设备树中的每一个组
imx_pinctrl_parse_groups:
将每个组中配置信息保存起来,保存在了每一个map中,与上面的结构体对应了起来。
就这样,将内核设备树中信息一个个组的保存起来。
总结一下函数的调用过程:
imx6ul_pinctrl_probe
imx_pinctrl_probe
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
imx_pinctrl_desc->owner = THIS_MODULE;
ret = imx_pinctrl_probe_dt(pdev, info);
info->nfunctions = nfuncs;
info->functions = devm_kzalloc...
info->groups = devm_kzalloc...
imx_pinctrl_parse_functions
for_each_child_of_node(np, child)
imx_pinctrl_parse_groups(child, grp, info, i++);
for (i = 0; i < grp->npins; i++) {
if (info->flags & IMX8_USE_SCU)
imx_pinctrl_parse_pin_scu(info, &grp->pin_ids[i],&grp->pins[i], list_p);
else
imx_pinctrl_parse_pin_mem(info, &grp->pin_ids[i],&grp->pins[i], list_p);
}
devm_pinctrl_register
really_probe
pinctrl_bind_pins
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
dev->pins->p = devm_pinctrl_get(dev);
pinctrl_get
create_pinctrl(dev);
ret = pinctrl_dt_to_map(p);
dt_to_map_one_config
ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
for_each_maps(maps_node, i, map) {
ret = add_setting(p, map);
}
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,PINCTRL_STATE_DEFAULT);
注意上面的调用过程,可以看到,
函数返回至pinctrl_bind_pins后,
调用pinctrl_select_state函数,将引脚配置为default_state或者init_state状态,也即初始状态。
根据是复用引脚还是配置引脚,调用对应的函数
最后就会操作到,内核提供的
各种函数结构,来处理复用或者配置的功能。