上一节我们了解了Pinctrl子系统主要的数据结构,要想更好的掌握Pinctrl子系统,还需要知道他的构造过程。
本节我们就来分析一下Pinctrl子系统的构造过程。
以内核面向对象的思想,设备树可以分为两部分,左边是Pinctrl子系统节点,用来描述Pincontroller,右边是使用Pincontroller的设备,本节我们就来分析左边Pinctrller的构造过程。
学习Pinctrl子系统时,抓住他的三大作用,就可以比较清楚的分析。
- 引脚的枚举与命名;
- 引脚复用;
- 引脚配置;
本节主要说明引脚的枚举与命名,他会涉及两种情况:单个引脚,多个引脚(group)。
以imx6ull为例。
单个引脚
根据 imx6ull 设备树中,Pinctrl 节点的 compatible 属性,可以找到对应的驱动文件。
对应的驱动文件是 pinctrl-imx6ul.c,对应的probe函数是 imx6ul_pinctrl_probe 函数。
从probe函数开始,分析一下处理流程:
data 如下,他有 pins 和 npins 成员,分表描述 单个引脚 和 引脚个数。
imx6ul_pinctrl_pads 是一个结构体数组,结构体类型为 pinctrl_pin_desc。
可以看到,imx6ul_pinctrl_pads 的每一个元素,都描述了一个引脚。
接着上面的流程,imx6ul_pinctrl_probe 函数会调用 imx_pinctrl_probe 函数,并且会传入data数据(引脚数据)。
引脚的数据会被存入 pinctrl_desc 结构体 的 pins 和 npins 成员,这样 pinctrl_desc 就保存了单个引脚的信息。
也可以在开发板中查看描述的引脚个数:
cat /sys/kernel/debug/pinctrl/20e0000.iomuxc/pins
0~128,共描述了 129 个引脚。
多个引脚
对于多个引脚(group),情况则略有不同。
通常,我们有两种方法来描述多个引脚(group):
- 代码中写死;
- 设备树中描述;
对于 imx6ull 来说,是在设备树中描述,由代码分析设备树,来动态的构造这些 group 的信息。
在 imx_pinctrl_probe 函数中,会调用 imx_pinctrl_probe_dt 函数,这个函数就会解析设备树,动态的构造 group 信息。
先不分析源码,先来看一下板子中的group信息:
cat /sys/kernel/debug/pinctrl/20e0000.iomuxc/pingroups
可以看到,有很多组group,这些group的信息就来自设备树。
然后,我们需要当前使用的设备树文件。
在 arch/arm/boot/dts 路径下,有一个 100ask_imx6ull-14x14.dtb,这个就是我们当前使用的 dtb 文件:
使用 dtc 工具,根据 dtb 文件,反编译生成 dts 文件:
dtc -I dtb -O dts 100ask_imx6ull-14x14.dtb > test.dts
在 dts 文件中,根据 compatible 值 "fsl,imx6ul-iomuxc",可以找到对应的 pinctrl 节点:
可以看到,设备树和设备中的 group 信息是一一对应的:
我们之前说,一组引脚可以用作功能A(function A),也可以用作功能B(function B)。
但是在imx6ull中,查看 /sys/kernel/debug/pinctrl/20e0000.iomuxc/pinmux-functions 文件,只显示了一种 functiono,imx6ul-evk,意思为 imx6ull 评估板,而不是具体说明功能的意思,这一点需要注意一下。(关于pinctrl的使用,每家厂商都各有不同,具体以代码实现为准。)
下面我们来看一下 imx_pinctrl_probe_dt 函数,imx_pinctrl_probe 函数中会调用 imx_pinctrl_probe_dt 函数,用来解析设备树,构造 group 信息,并且解析获得的信息都会存入一个 info 变量中,他是一个结构体指针,指向 imx_pinctrl_soc_info 结构体:
根据设备树信息,解析 imx_pinctrl_probe_dt 函数流程。
由于 iomuxc 节点下没有"fsl,pins",而 iomuxc 节点的子节点,imx6ul-evk 节点的子节点中有 "fsl,pins",所以 imx_pinctrl_dt_is_flat_functions 会返回 false,所以 nfuncs = 1,即 function 数量为1。
保存 funtion 数量:
计算所有 function 中的 group 总数,并且为这些 group 分配内存空间,由于只有一个function imx6ul-evk,所以这里实际就是计算 imx6ul-evk 节点的子节点数:
分配完空间,对于每个 function,调用 imx_pinctrl_parse_functions 函数,根据节点数据,解析funtion 信息:
解析一下 imx_pinctrl_parse_functions 函数,在 imx_pinctrl_parse_functions 函数中,最终会调用 imx_pinctrl_parse_groups 函数,来解析获取 group 信息:
imx_pinctrl_parse_groups 函数中,类似的,最终会调用 imx_pinctrl_parse_pin_mem 函数来解析引脚:
在 imx_pinctrl_parse_pin_mem 函数中,则会根据 "fsl,pins"属性的值,来填充 imx_pin_memmap 结构体:
大致总结如下图: