【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程

相关文章

  1. 《【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程》
  2. 《【Linux】基于IMX6ULL平台Pinctrl架构分析总结(二)-- Client Device使用过程》

1. 前言

在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。通过pinctrl子系统来设置引脚的复用、配置,可以将IO复用成GPIO、I2C等其它功能。

本篇文章主要是基于IMX6ULL平台来分析Pin Controller子系统整个注册过程。整个pinctrl子系统比较核心的结构体是struct pinctrl_dev,后面会详细一步步介绍它整个构造过程。下面是经过分析总结的图,如下:
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第1张图片
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第2张图片

2.Pin Controller和Clinet Device

Device Tree中,Pinctrl主要分为2个部分:Pin ControllerClinet Device。下面截图只是一个简单的Demo,说明它们之间的关系。Device可能会有多个状态,不同的状态下,Pin的状态的作用可能不相同。default对应配置pinctrl-0idle对应配置pinctrl-1。在Client Device的default状态下,它的配置pinctrl-0指向了Pin Controller的state_0_node_a,作为uart0功能。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第3张图片
下面截图是在IMX6ULL中实际使用的Pinctrl,后面的代码的分析主要是以I2C1作为例子,它是如何一步步构建出一个Pinctrl子系统的。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第4张图片

3.源码分析

源码分析主要介绍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中。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第5张图片
Path:Kernel\drivers\pinctrl\freescale\pinctrl-imx.c
imx_pinctrl_probe中,主要分为以下几个步骤:

  • 初始化struct pinctrl_desc,赋值imx pin 控制/复用/配置能力的操作方法。
  • 注册并且初始化pinctrl
  • 从设备树中解析pinctrl相关信息
  • 使能pin controller

下面是根据imx_pinctrl_probe分析总结它们之间的关系,如下:
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第6张图片

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的相关操作方法。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第7张图片

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。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第8张图片

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。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第9张图片

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中的数据 。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第10张图片

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中,方便后续使用时直接调用。
【Linux】基于IMX6ULL平台Pinctrl架构分析总结(一)-- Pin Controller注册过程_第11张图片

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;
}

4. 总结

经过以上整个过程,所有的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;
}

5. 分析过程的笔记草图

下面的截图是分析过程做的笔记,可以参考一起看。

你可能感兴趣的:(Linux,linux,Pinctrl)