Linux pinctrl子系统框架流程详解(基于Kernel 3.16,arm,设备树)

    以下讲的pinctrl子系统框架包括3点,1. pinctrl子系统简介;2.pinctrl子系统的注册;3. 设备驱动匹配时,probe执行前的管脚自动配置。写博客不易,如若转载,请注明出处。

一、pinctrl子系统简介

    在arm的各种soc芯片中,往往可以看到1个pin引脚,既可以作为GPIO,也可以作为spi、i2c、uart总线中的1根引脚,即该引脚是可以复用为不同功能的引脚。Linux内核引入的pinctrl子系统,目的正是为了统一各soc厂商的pin脚管理。

Pinctrl子系统提供主要功能有:
    (1)管理系统中所有可控的pin,在pinctrl子系统注册时,标识这些pin。

    (2)管理这些pin的复用(mux)。通过pin function、pin group的搭配选择来管理一组的pin,定义该组pin为特定的功能。

    (3)配置每组pin内每个pin的特性。例如配置pin的上拉、下拉电阻,配置pin的driver strength等。

二、pinctrl子系统的注册

    以pfc-r8a7791(见Core.c (drivers\pinctrl\sh-pfc))为例。从入口postcore_initcall(sh_pfc_init);分析,使用postcore_initcall而不是module_init,是因为pinctrl的注册要在其他驱动注册之前完成,各设备(如SOC片内的各个控制器)使用的引脚功能依赖于pinctrl子系统提供的引脚复用服务。其他驱动的注册,设备驱动匹配后执行probe前,pinctrl子系统会自动配置设备需要的引脚复用。


这里注册的是一个平台驱动,看.of_match_table = of_match_ptr(sh_pfc_of_table),

static const struct of_device_id sh_pfc_of_table[] = {

。。。
    {
        .compatible = "renesas,pfc-r8a7791",
        .data = &r8a7791_pinmux_info,
    },
。。。

}
MODULE_DEVICE_TABLE(of, sh_pfc_of_table);

见 设备树r8a7791.dtsi 里面的结点:

    pfc: pfc@e6060000 {
        compatible = "renesas,pfc-r8a7791";
        reg = <0 0xe6060000 0 0x250>;
        #gpio-range-cells = <3>;
    };

得知以上设备驱动将匹配成功,匹配后,执行sh_pfc_probe,进入该函数

static int sh_pfc_probe(struct platform_device *pdev)
{
。。。
	ret = sh_pfc_register_pinctrl(pfc);
	if (unlikely(ret != 0))
		goto error;
。。。
	return 0;
。。
}

一开始是资源的获取、准备,继续进入sh_pfc_register_pinctrl:


int sh_pfc_register_pinctrl(struct sh_pfc *pfc)
{
	struct sh_pfc_pinctrl *pmx;
	int ret;

	pmx = devm_kzalloc(pfc->dev, sizeof(*pmx), GFP_KERNEL);
	if (unlikely(!pmx))
		return -ENOMEM;

	pmx->pfc = pfc;
	pfc->pinctrl = pmx;

	ret = sh_pfc_map_pins(pfc, pmx);
	if (ret < 0)
		return ret;

	pmx->pctl_desc.name = DRV_NAME;
	pmx->pctl_desc.owner = THIS_MODULE;
	pmx->pctl_desc.pctlops = &sh_pfc_pinctrl_ops;
	pmx->pctl_desc.pmxops = &sh_pfc_pinmux_ops;
	pmx->pctl_desc.confops = &sh_pfc_pinconf_ops;
	pmx->pctl_desc.pins = pmx->pins;
	pmx->pctl_desc.npins = pfc->info->nr_pins;

	pmx->pctl = pinctrl_register(&pmx->pctl_desc, pfc->dev, pmx);
	if (pmx->pctl == NULL)
		return -EINVAL;

	return 0;
}

这里可以看到,pinctrl_register前所进行的准备:struct pinctrl_desc中pctlops、pmxops、confops各个pin相关操作函数接口的实现,以及pins、npins 系统所有可控pin的前期准备。进入pinctrl_register函数:

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
				    struct device *dev, void *driver_data)
{
	struct pinctrl_dev *pctldev;
	int ret;
。。。
	pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
。。。
	/* Initialize pin control device struct */
	pctldev->owner = pctldesc->owner;
	pctldev->desc = pctldesc;
	pctldev->driver_data = driver_data;
	INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
	INIT_LIST_HEAD(&pctldev->gpio_ranges);
	pctldev->dev = dev;
	mutex_init(&pctldev->mutex);
。。。
	/* Register all the pins */
	dev_dbg(dev, "try to register %d pins ...\n",  pctldesc->npins);
	ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);  ----为pins数组中的每个pin分配struct pin_desc,并进行赋值,struct pin_desc是pinctrl子系统用来进行每个pin管理的最小单元
。。。
	mutex_lock(&pinctrldev_list_mutex);
	list_add_tail(&pctldev->node, &pinctrldev_list);
	mutex_unlock(&pinctrldev_list_mutex);

	pctldev->p = pinctrl_get(pctldev->dev);   ----重要函数

	if (!IS_ERR(pctldev->p)) {
		pctldev->hog_default =
			pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);   ----选dev结点状态为默认的pin
。。。
		pinctrl_select_state(pctldev->p,
				pctldev->hog_default))  ----进行dev结点默认状态pin引脚功能的使能
。。。
		}

		pctldev->hog_sleep =
			pinctrl_lookup_state(pctldev->p,
						    PINCTRL_STATE_SLEEP);
。。。
	}

	pinctrl_init_device_debugfs(pctldev);

	return pctldev;
。。。
}

分配一个struct pinctrl_dev并初始化,一般系统只做一次pinctrl_register,因此struct pinctrl_dev在系统中一般只有一个。pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);会为pins数组中的每个pin分配独立的struct pin_desc,并进行赋值。struct pin_desc是pinctrl子系统用来管理每个pin的最小单元。继续看pinctrl_get函数:

struct pinctrl *pinctrl_get(struct device *dev)
{
	struct pinctrl *p;

	if (WARN_ON(!dev))
		return ERR_PTR(-EINVAL);

	/*
	 * See if somebody else (such as the device core) has already
	 * obtained a handle to the pinctrl for this device. In that case,
	 * return another pointer to it.
	 */
	p = find_pinctrl(dev);  ----首次调用,这里p==NULL
	if (p != NULL) {
		dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
		kref_get(&p->users);
		return p;
	}

	return create_pinctrl(dev);
}

进入create_pinctrl函数:

static struct pinctrl *create_pinctrl(struct device *dev)
{
	struct pinctrl *p;
	const char *devname;
	struct pinctrl_maps *maps_node;
	int i;
	struct pinctrl_map const *map;
	int ret;

	/*
	 * create the state cookie holder struct pinctrl for each
	 * mapping, this is what consumers will get when requesting
	 * a pin control handle with pinctrl_get()
	 */
	p = kzalloc(sizeof(*p), GFP_KERNEL);  ----对于每个设备节点,都会分配一个struct pinctrl。可以在dd.c中really_probe内找到pinctrl_bind_pins,里面有devm_pinctrl_get,由此可知对于每个设备节点,都会分配一个struct pinctrl
	if (p == NULL) {
		dev_err(dev, "failed to alloc struct pinctrl\n");
		return ERR_PTR(-ENOMEM);
	}
	p->dev = dev;
	INIT_LIST_HEAD(&p->states);
	INIT_LIST_HEAD(&p->dt_maps);

	ret = pinctrl_dt_to_map(p);  ----这里用来获取dtb节点中pinctrl-0,pinctrl-1。。。指向的pinctrl-dev子节点的function、group信息,构建dt_maps链表
	if (ret < 0) {
		kfree(p);
		return ERR_PTR(ret);
	}

	devname = dev_name(dev);

	mutex_lock(&pinctrl_maps_mutex);
	/* Iterate over the pin control maps to locate the right ones */
	for_each_maps(maps_node, i, map) {
		/* Map must be for this device */
		if (strcmp(map->dev_name, devname))
			continue;

		ret = add_setting(p, map);  ----创建struct pinctrl_state(有3种state:“default”、“sleep”、“idle”)并加入struct pinctrl的states链表中,通过对构建好的dt_maps链表进行分析,会相应的以链表的形式挂载在这3种state中。以后的pin操作就是用state的状态来统一控制的
		/*
		 * At this point the adding of a setting may:
		 *
		 * - Defer, if the pinctrl device is not yet available
		 * - Fail, if the pinctrl device is not yet available,
		 *   AND the setting is a hog. We cannot defer that, since
		 *   the hog will kick in immediately after the device
		 *   is registered.
		 *
		 * If the error returned was not -EPROBE_DEFER then we
		 * accumulate the errors to see if we end up with
		 * an -EPROBE_DEFER later, as that is the worst case.
		 */
		if (ret == -EPROBE_DEFER) {
			pinctrl_free(p, false);
			mutex_unlock(&pinctrl_maps_mutex);
			return ERR_PTR(ret);
		}
	}
	mutex_unlock(&pinctrl_maps_mutex);

	if (ret < 0) {
		/* If some other error than deferral occured, return here */
		pinctrl_free(p, false);
		return ERR_PTR(ret);
	}

	kref_init(&p->users);

	/* Add the pinctrl handle to the global list */
	mutex_lock(&pinctrl_list_mutex);
	list_add_tail(&p->node, &pinctrl_list);
	mutex_unlock(&pinctrl_list_mutex);

	return p;
}

里面实在庞大,不再继续追踪下去了~_~

贴几张关系图:

1. pinctrl子系统概要图

pinctrl子系统概要图
pinctrl子系统概要图

2. pinctrl子系统数据结构图1

pinctrl子系统数据结构图1
pinctrl子系统数据结构图1

3. pinctrl子系统数据结构图2

pinctrl子系统数据结构图2
pinctrl子系统数据结构图2

4. GPIO子系统(通过gpio_pin_range关联)

GPIO子系统(通过gpio_pin_range关联)
GPIO子系统(通过gpio_pin_range关联)

 

三、设备驱动匹配时,probe执行前的管脚自动配置

    pinctrl子系统有一个重要的功能,在设备树中填好引脚复用信息后,它能够在设备驱动匹配时,自动地给你配置好设备的引脚,而不需要你在驱动的代码中为设备(如SOC的片内控制器)去配置它所需要的引脚。

    设备驱动匹配时,将执行really_probe函数(dd.c (drivers\base)),里面的pinctrl_bind_pins即去自动配置好设备的引脚。执行完之后,再执行probe。看pinctrl.c (drivers\base)的pinctrl_bind_pins函数:

int pinctrl_bind_pins(struct device *dev)
{
	int ret;

	dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
。。。
	dev->pins->p = devm_pinctrl_get(dev);
。。。
	dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_DEFAULT);  ----查找“default”的默认state,赋给default_state
。。。
	ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);  ----选中、使能“default”状态的引脚
。。。
#ifdef CONFIG_PM  ----电源管理相关
。。。
	dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_SLEEP);  ----查找“sleep”的state,赋给sleep_state
	if (IS_ERR(dev->pins->sleep_state))
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no sleep pinctrl state\n");

	dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
					PINCTRL_STATE_IDLE);  ----查找“idle”的state,赋给idle_state
	if (IS_ERR(dev->pins->idle_state))
		/* Not supplying this state is perfectly legal */
		dev_dbg(dev, "no idle pinctrl state\n");
#endif

	return 0;
。。。
}

 

  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
引用\[1\]: pinctrl子系统Linux内核中的一个框架,用于统一管理不同系统芯片的引脚控制和配置。在pinctrl子系统中,有一个主要的结构体struct pinctrl_dev,用于表示一个引脚控制器设备。一般系统只会有一个struct pinctrl_dev实例。通过调用pinctrl_register_pins函数,可以为每个引脚分配一个独立的struct pin_desc结构体,并进行相应的赋值。struct pin_desc是pinctrl子系统用来管理每个引脚的最小单元。 引用\[2\]: 在pinctrl子系统中,引脚控制器并不只有一个,而是可以有多个。在设备树中,含有pinctrl-names和pinctrl-0属性的节点会拥有一个struct pinctrl结构体。pinctrl-names和pinctrl-0属性中的内容指定了要控制的引脚。有时候会遇到多个pinctrl-names和pinctrl-0属性,可以通过这种方式来配置不同的引脚控制。 引用\[3\]: pinctrl子系统的目的是为了统一各种不同的系统芯片中的引脚管理。在ARM的各种SOC芯片中,一个引脚可以被复用为不同功能的引脚,例如GPIO、SPI、I2C、UART等。pinctrl子系统的引入正是为了解决这种复用问题,使得不同SOC厂商的引脚管理能够在Linux内核中得到统一。 #### 引用[.reference_title] - *1* *3* [Linux pinctrl子系统框架流程详解(基于Kernel 3.16arm设备树)](https://blog.csdn.net/ZHONGkunjia/article/details/89873417)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于LinuxPinctrl子系统框架源码分析](https://blog.csdn.net/qq_42017846/article/details/127795402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒江独钓2009

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值