在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。通过pinctrl子系统来设置引脚的复用、配置,可以将IO复用成GPIO、I2C等其它功能。
本篇文章主要是基于IMX6ULL平台来分析Pin Controller子系统整个注册过程。整个pinctrl子系统比较核心的结构体是struct pinctrl_dev
,后面会详细一步步介绍它整个构造过程。下面是经过分析总结的图,如下:
在Device Tree中,Pinctrl主要分为2个部分:Pin Controller和Clinet Device。下面截图只是一个简单的Demo,说明它们之间的关系。Device可能会有多个状态,不同的状态下,Pin的状态的作用可能不相同。default
对应配置pinctrl-0
,idle
对应配置pinctrl-1
。在Client Device的default
状态下,它的配置pinctrl-0
指向了Pin Controller的state_0_node_a
,作为uart0功能。
下面截图是在IMX6ULL中实际使用的Pinctrl,后面的代码的分析主要是以I2C1作为例子,它是如何一步步构建出一个Pinctrl子系统的。
源码分析主要介绍Pin Controller整个注册过程,也就是fsl,imx6ul-iomuxc
的注册过程。NOTE:下面分析代码的顺序就是代码执行的顺序。
Path:Kernel\drivers\pinctrl\freescale\pinctrl-imx6ul.c
在Device Tree中,发现Pin Controller的name是fsl,imx6ul-iomuxc
,然后在源代码中搜索追踪到pinctrl-imx6ul.c。在imx6ul_pinctrl_pads
数组中,给每个pin都保存一个name和一个序号number。然后将它注册到imx_pinctrl_probe
中。
Path:Kernel\drivers\pinctrl\freescale\pinctrl-imx.c
在imx_pinctrl_probe
中,主要分为以下几个步骤:
下面是根据imx_pinctrl_probe
分析总结它们之间的关系,如下:
int imx_pinctrl_probe(struct platform_device *pdev,
const struct imx_pinctrl_soc_info *info)
{
struct pinctrl_desc *imx_pinctrl_desc;
struct imx_pinctrl *ipctl;
/* 给struct imx_pinctrl申请空间 */
ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL);
/* 根据info->npis给每个pin申请struct imx_pin_reg空间 */
imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc),GFP_KERNEL);
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins; // 将imx6ul_pinctrl_pads地址赋值给pins
imx_pinctrl_desc->npins = info->npins; // 将imx6ul_pinctrl_pads的个数赋值给npins
imx_pinctrl_desc->pctlops = &imx_pctrl_ops; // imx pin 控制的操作方法
imx_pinctrl_desc->pmxops = &imx_pmx_ops; // imx pin 复用的操作方法
imx_pinctrl_desc->confops = &imx_pinconf_ops; // imx pin 配置能力的操作方法
imx_pinctrl_desc->owner = THIS_MODULE;
ipctl->info = info;
ipctl->dev = &pdev->dev;
/* 保存ipctrl数据到struct device的driver_data中 */
platform_set_drvdata(pdev, ipctl);
/* 注册并且初始化pinctrl */
ret = devm_pinctrl_register_and_init(&pdev->dev,
imx_pinctrl_desc, ipctl,
&ipctl->pctl);
/* 从设备树中解析pinctrl相关信息 */
ret = imx_pinctrl_probe_dt(pdev, ipctl);
return pinctrl_enable(ipctl->pctl);
}
Path:Kernel\drivers\pinctrl\core.c
在pinctrl_init_controller
申请了struct pinctrl_dev
pin control device 结构体空间,并且进行初始化。在pinctrl_dev中的pinctrl_desc被初始化后,就可以通过pinctrl_dev访问imx6ull所有的pin数据和pin的相关操作方法。
static struct pinctrl_dev *
pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev,
void *driver_data)
{
struct pinctrl_dev *pctldev;
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
/* 初始化 pin control device 结构体 */
pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
/* 注册所有的pin */
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
return pctldev;
}
Path:Kernel\drivers\pinctrl\core.c
在pinctrl_register_pins
中,会将imx6ul_pinctrl_pads数组里面所有的pin,以pin_desc方式注册到核心结构体的pinctrl_dev中。struct radix_tree_root pin_desc_tree
中所在的位置就是pin的序列号number,name就是pin的name。
static int pinctrl_register_pins(struct pinctrl_dev *pctldev,
const struct pinctrl_pin_desc *pins,
unsigned num_descs)
{
unsigned i;
int ret = 0;
/* 注册imx6ul_pinctrl_pads数组里面所有的pin */
for (i = 0; i < num_descs; i++) {
ret = pinctrl_register_one_pin(pctldev, &pins[i]);
if (ret)
return ret;
}
return 0;
}
static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,
const struct pinctrl_pin_desc *pin)
{
struct pin_desc *pindesc;
/* 给每个pin申请pin_desc空间,并且初始化 */
pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL);
pindesc->pctldev = pctldev;
pindesc->name = pin->name; // MX6UL_PAD_GPIO1_IO00、MX6UL_PAD_GPIO1_IO01......
pindesc->drv_data = pin->drv_data;
/* 将每个pin_desc添加核心结构体的pinctrl_dev中 */
radix_tree_insert(&pctldev->pin_desc_tree, pin->number, pindesc);
return 0;
}
Path:Kernel\drivers\pinctrl\freescale\pinctrl-imx.c
在imx_pinctrl_probe_dt
中,function其实就是iomuxc@20e0000。申请一个struct function_desc空间,然后将function添加到核心结构体pinctrl_dev的pin_function_tree中。统计dts设备树里面的iomuxc@20e0000的node节点个数,并且解析该function。
static int imx_pinctrl_probe_dt(struct platform_device *pdev,
struct imx_pinctrl *ipctl)
{
struct pinctrl_dev *pctl = ipctl->pctl;
/* 这里的function其实就是iomuxc@20e0000 */
for (i = 0; i < nfuncs; i++) { // nfuncs = 1
struct function_desc *function;
/* 申请一个struct function_desc空间 */
function = devm_kzalloc(&pdev->dev, sizeof(*function),
GFP_KERNEL);
/* 将function添加到核心结构体pinctrl_dev的pin_function_tree中 */
radix_tree_insert(&pctl->pin_function_tree, i, function);
}
pctl->num_functions = nfuncs; // num_functions = 1
/* num_groups等于dts设备树里面的iomuxc@20e0000的node节点个数 */
pctl->num_groups = of_get_child_count(np);
/* 解析指定的function,这里只有1个。 */
imx_pinctrl_parse_functions(np, ipctl, 0);
return 0;
}
Path:Kernel\drivers\pinctrl\freescale\pinctrl-imx.c
从Pinctrl核心结构体pinctrl_dev的pin_function_tree
中获取function iomuxc@20e0000,然后获取group的个数,给每个group申请struct group_desc空间,并且将group添加到Pinctrl核心结构体pinctrl_dev的pin_group_tree
中。最后,解析析iomuxc@20e0000的每个group中的数据 。
static int imx_pinctrl_parse_functions(struct device_node *np,
struct imx_pinctrl *ipctl,
u32 index)
{
struct function_desc *func;
/* 从pin_function_tree中获取func地址 */
func = pinmux_generic_get_function(pctl, index);
func->name = np->name; // name = iomuxc
/* 获取iomuxc@20e0000组的个数 */
func->num_group_names = of_get_child_count(np);
/* 给每个group name存储地址申请空间 */
func->group_names = devm_kcalloc(ipctl->dev, func->num_group_names,
sizeof(char *), GFP_KERNEL);
/* 将iomuxc@20e0000的每个node子节点存到pin_group_tree */
for_each_child_of_node(np, child) {
func->group_names[i] = child->name; // name = csi1grp、enet1grp、enet1grp.......
grp = devm_kzalloc(ipctl->dev, sizeof(struct group_desc),
GFP_KERNEL);
/* 将每个grpup_desc添加pinctrl_dev的pin_group_tree中 */
radix_tree_insert(&pctl->pin_group_tree, ipctl->group_index++, grp);
/* 解析iomuxc@20e0000的每个group中的数据 */
imx_pinctrl_parse_groups(child, grp, ipctl, i++);
}
return 0;
}
Path:Kernel\drivers\pinctrl\freescale\pinctrl-imx.c
在imx_pinctrl_parse_groups
中,解析Device Tree Function中所有Group的信息,如:csi1grp、enet1grp、enet1grp…。这里会将每个pin的配置保存到struct imx_pin等对应的数据结构,最终挂载到Pinctrl核心结构体pinctrl_dev的pin_group_tree
中,方便后续使用时直接调用。
static int imx_pinctrl_parse_groups(struct device_node *np,
struct group_desc *grp,
struct imx_pinctrl *ipctl,
u32 index)
{
/* name = csi1grp、enet1grp、enet1grp....... */
grp->name = np->name;
/* 获取设备树中的fsl,pins属性数据list */
list = of_get_property(np, "fsl,pins", &size);
/* 获取该组pin的个数,并给每个pin申请空间 */
grp->num_pins = size / pin_size;
grp->data = devm_kcalloc(ipctl->dev,grp->num_pins, sizeof(struct imx_pin),GFP_KERNEL);
grp->pins = devm_kcalloc(ipctl->dev,grp->num_pins, sizeof(unsigned int),GFP_KERNEL);
for (i = 0; i < grp->num_pins; i++) {
/* 获取pin存放数据的地址 */
pin = &((struct imx_pin *)(grp->data))[i];
/* 将list每个pin的配置保存到struct imx_pin等对应的数据结构中,方便后续使用时直接调用 */
imx_pinctrl_parse_pin_mmio(ipctl, &grp->pins[i],pin, &list, np);
}
return 0;
}
经过以上整个过程,所有的Device Tree中Pin相关信息都可以通过Pinctrl核心结构体pinctrl_dev
拿到。???疑问:后面Pinctrl Client Device需要使用pin是又怎么拿到保存所有pin信息的pinctrl_dev的呢???因为我们还有一个函数pinctrl_enable()
没有分析,它会将前面配置好的struct pinctrl_dev
,保存到全局List pinctrldev_list中。如果后面有谁需要使用,就只需要遍历这个List就可以了。
Path:Kernel\drivers\pinctrl\core.c
--->imx_pinctrl_probe()
--->pinctrl_enable()
/* Global list of pin control devices (struct pinctrl_dev) */
static LIST_HEAD(pinctrldev_list);
int pinctrl_enable(struct pinctrl_dev *pctldev)
{
error = pinctrl_claim_hogs(pctldev);
/* 将新的struct pinctrl_dev添加到pinctrldev_list中 */
list_add_tail(&pctldev->node, &pinctrldev_list);
/* 在debugfs的/sys/kernel/debug/目录下,创建pinctrl调试节点 */
pinctrl_init_device_debugfs(pctldev);
return 0;
}
下面的截图是分析过程做的笔记,可以参考一起看。