以下讲的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==NULLif (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 pinctrlif (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子系统概要图
2.pinctrl子系统数据结构图1
3.pinctrl子系统数据结构图2
4.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_stateif (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_stateif (IS_ERR(dev->pins->idle_state))/* Not supplying this state is perfectly legal */dev_dbg(dev, "no idle pinctrl state\n");#endifreturn 0;。。。}
如果觉得《Linux pinctrl子系统框架流程详解(基于Kernel 3.16 arm 设备树)》对你有帮助,请点赞、收藏,并留下你的观点哦!