1 pinctrl和gpio subsystem引入
上一节引入gpio了子系统:
linux内核驱动-gpio子系统 - fuzidage - 博客园 (cnblogs.com)
字符设备驱动-gpio子系统 | Hexo (fuzidage.github.io)
Linux 驱动讲究驱动分离与分层,pinctrl 和 gpio 子系统就是驱动分离与分层思想下的产物。
pinctrl顾名思义就是引脚控制,用来配置比如引脚mux复用信息,引脚电器属性(比如上/下拉、速度、驱动能力等)信息。
gpio顾名思义就是控制gpio的输入输出,以及高低电平。不过,大多数的芯片并没有单独的IOMUX模块,引脚的复用、配置等,而是在GPIO模块内部实现的。
2 pinctrl子系统原理介绍
2.1 pinctrl子系统
2.1.1 pinctrl子系统软件架构
pinctrl子系统源码路径是linux/drivers/pinctrl:
1 2 3 4
| 其他驱动层(client):具体到使用系统pin资源的设备驱动程序 pinctrl核心层(core):内核抽象出来,向下为SoC pin controler drvier提供底层通信接口的能力, 向上为其他驱动提供了控制pin的能力,比如pin复用、配置引脚的电气特性,同时也为GPIO子系统提供pin操作。 pin控制器驱动层(pinctrl-driver): 提供了操作pin的具体方法。
|
pinctrl-driver
主要为pinctrl-core
提供pin的操作能力。把系统所有的pin以及对于pin的控制接口实例化成pinctrl_desc
,并将pinctrl_desc
注册到pinctrl-core
中去。
2.1.2 Pinctrl重要概念
Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
有介绍重要相关概念:
1. pin controller:
芯片手册里你找不到 pin controller
,它是一个软件上的概念。对应 IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)。
2. client device:
“客户设备”,客户是指Pinctrl
系统的客户,即使用Pinctrl系统的设备,使用引脚的设备
- pin state
对于一个"client device"
,如UART设备,它有多个“状态”
:default、sleep
等,那么对应的引脚也有这些状态。
比如,默认状态下,UART设备正常工作,那么所用的引脚就要复用为UART功能;
休眠状态下,为了省电,可以把这些引脚复用为GPIO功能;或者直接把它们配置输出高电平。
上图pinctrl-names
定义2种状态:default,sleep
。
第0种状态用到的引脚在pinctrl-0
中定义,它是state_0_node_a
,位于pincontroller
节点中。
第1种状态用到的引脚在Pinctrl-1
中定义,它是state_1_node_a
,位于pincontroller
节点中。
当UART设备处于default
状态时,pinctrl子系统会自动根据上述信息将所用引脚复用为uart0功能。
当UART设备处于sleep
状态时,pinctrl子系统会自动根据上述信息将所用引脚配置为高电平。
- groups和function
一个设备会用到一个或多个引脚,这些引脚可以归纳为一组(group)
;
这些引脚可以复用为某个功能:function
,如I2C功能,SPI功能,GPIO功能等。当然:一个设备可以用到多组引脚,比如A1、A2两组引脚,A1组复用为F1功能,A2组复用为F2功能:
- Generic pin multiplexing node和Generic pin configuration node
下图左边pin controller
节点中,有子节点或孙节点,它们是给client device
使用的。
可用来描述复用信息:哪组(group)
引脚复用为哪个功能(function)
;
配置信息:哪组(group)
引脚配置为哪个设置功能(setting
),如上拉、下拉等;
2.1.3 pinctrl 子系统注册流程
以imx6ull为例,drivers/pinctrl/freescale/pinctrl-imx6ul.c
驱动的入口是 arch_initcall
中声明的函数,类似于我们经常写的 module_init
是动态ko加载,arch_initcall
是编译进入内核镜像。可以看到是利用platform_device框架来写的。
根据 compatible
和设备树的compatible
字段进行匹配,匹配成功执行 probe 函数,调用imx_pinctrl_probe_dt
解析dts中的pinctrl
描述信息。调用pinctrl_register
或者devm_pinctrl_register
注册pinctl子系统。
3 soc pinctrl主要结构体(controller)
3.0 数据结构关系图
总框图记录数据结构之间的关联。
1 2 3 4 5 6 7 8 9 10
| drivers\pinctrl\core.h include\linux\pinctrl\pinctrl.h include\linux\pinctrl\pinmux.h include\linux\pinctrl\pinconf.h
drivers\pinctrl\core.h include\linux\pinctrl\devinfo.h include\linux\device.h include\linux\pinctrl\machine.h
|
3.1 pinctrl_dev
pinctrl_dev
是 pinctrl 子系统的根源结构体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| struct pinctrl_dev { struct list_head node; struct pinctrl_desc *desc; struct radix_tree_root pin_desc_tree; #ifdef CONFIG_GENERIC_PINCTRL_GROUPS struct radix_tree_root pin_group_tree; unsigned int num_groups; #endif #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS struct radix_tree_root pin_function_tree; unsigned int num_functions; #endif struct list_head gpio_ranges; struct device *dev; struct module *owner; void *driver_data; struct pinctrl *p; struct pinctrl_state *hog_default; struct pinctrl_state *hog_sleep; struct mutex mutex; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif }
|
/driver/pinctrl/core.c
中注册pinctrl时将soc中所有的pinctrl_dev
挂载到pinctrl_dev_list
链表中方便查询使用。
1 2
| struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data);
|
3.2 pinctrl_desc
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct pinctrl_desc { const char *name; const struct pinctrl_pin_desc *pins; unsigned int npins; const struct pinctrl_ops *pctlops; const struct pinmux_ops *pmxops; const struct pinconf_ops *confops; struct module *owner; #ifdef CONFIG_GENERIC_PINCONF unsigned int num_custom_params; const struct pinconf_generic_params *custom_params; const struct pin_config_item *custom_conf_items; #endif };
|
三个ops:
group
操作接口对应数据结构struct pinctrl_ops
,包含get_groups_count
、get_group_name
、get_group_pins
等接口;
mux
操作接口对应的数据结构为struct pinmux_ops
,包含pin request
、free
、set_mux
、get_functions_count
、get_function_groups
等;
function
操作接口对应的数据结构为struct pinconf_ops
,包含pin_config_set
、pin_config_get
、pin_config_group_get
、pin_config_group_set
等接口;
3.2.1 pinctrl_pin_desc
1 2 3 4 5
| struct pinctrl_pin_desc { unsigned number; const char *name; void *drv_data; };
|
pinctrl_pin_desc
来描述一个引脚.
3.2.2 pin_desc
1 2 3 4 5 6 7 8 9 10 11 12 13
| struct pin_desc { struct pinctrl_dev *pctldev; const char *name; bool dynamic_name; void *drv_data; #ifdef CONFIG_PINMUX unsigned mux_usecount; const char *mux_owner; const struct pinctrl_setting_mux *mux_setting; const char *gpio_owner; #endif };
|
记录引脚的使用计数、引脚当前所属的function、group
信息(该数据结构主要是pinctrl子系统用于判断一个引脚是否被多次配置不同的复用情况使用(pin_request、pin_free
)
3.2.3 三个ops
3.2.3.1 pinctrl_ops
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct pinctrl_ops { int (*get_groups_count) (); const char *(*get_group_name) (); int (*get_group_pins) (); void (*pin_dbg_show) (); int (*dt_node_to_map) (); void (*dt_free_map) (); };
|
- 来取出某组的引脚:
get_groups_count、get_group_pins
- 处理设备树中
pin controller
中的某个节点创建映射:dt_node_to_map
,把device_node转换为一系列的pinctrl_map
3.2.3.2 pinmux_ops
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| struct pinconf_ops { #ifdef CONFIG_GENERIC_PINCONF bool is_generic; #endif int (*pin_config_get) (); int (*pin_config_set) (); int (*pin_config_group_get) (); int (*pin_config_group_set) (); int (*pin_config_dbg_parse_modify) (); void (*pin_config_dbg_show) (); void (*pin_config_group_dbg_show) (); void (*pin_config_config_dbg_show) (s); };
|
3.2.3.3 pinconf_ops
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| struct pinconf_ops { #ifdef CONFIG_GENERIC_PINCONF bool is_generic; #endif int (*pin_config_get) (); int (*pin_config_set) (); int (*pin_config_group_get) (); int (*pin_config_group_set) (); int (*pin_config_dbg_parse_modify) (); void (*pin_config_dbg_show) (); void (*pin_config_group_dbg_show) (); void (*pin_config_config_dbg_show) (s); };
|
4 board pinctrl相关结构体(使用者client)
4.0 数据结构关系
前面Soc pin描述相关的数据结构,已经搭建了该soc所支持的pin
、function
、group
以及相关操作接口等信息;而board pin
描述相关的数据结构则描述一块board所使用到的function
及相关的group
:
4.1 pinctrl_map
1 2 3 4 5 6 7 8 9 10 11 12
| struct pinctrl_map { const char *dev_name; const char *name; enum pinctrl_map_type type; const char *ctrl_dev_name; union { struct pinctrl_map_mux mux; struct pinctrl_map_configs configs; } data; };
|
该数据结构可以理解为一个function类型:
4.2 dev_pin_info
device结构体中有一个dev_pin_info
,用来保存设备的pinctrl信息:
1 2 3 4 5 6 7 8 9 10
| struct dev_pin_info { struct pinctrl *p; struct pinctrl_state *default_state; struct pinctrl_state *init_state; #ifdef CONFIG_PM struct pinctrl_state *sleep_state; struct pinctrl_state *idle_state; #endif };
|
4.2.1 pinctrl
一个设备的所有引脚配置相关的信息:
1 2 3 4 5 6 7 8
| struct pinctrl { struct list_head node; struct device *dev; struct list_head states; struct pinctrl_state *state; struct list_head dt_maps; struct kref users; };
|
4.2.1.1 pinctrl_state
1 2 3 4 5
| struct pinctrl_state { struct list_head node; const char *name; struct list_head settings; };
|
5 pinctrl实例化示例
5.1 pin controller示例
打开imx6ull的dtsi找到pin controller
控制器节点,对比IMX6ULL参考手册可知:imx6ull一共3个IOMUX控制器
:
5.2 client示例
以evk
公板为例:
imx6ull pinmux dts配置描述:
我们看到:
1
| MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
|
它是表示什么意思呢?配置了哪些信息呢?
UART1_RTS_B
复用成GPIO1_IO19
:
此宏定义在imx_6ull_pinfunc.h, 后面跟着 5 个数字,也就是这个宏定义的具体值,如下所示:
1
| 0x0090 0x031C 0x0000 0x5 0x0
|
这 5 个值的含义如下所示:
1
| <mux_reg conf_reg input_reg mux_mode input_val>
|
0x0090表示UART1_RTS_B
的iomux寄存器
0x031C表示UART1_RTS_B
的电器属性寄存器
0x0000表示input寄存器
0x5表示复用成模式5
0x0表示input寄存器设置值为0x0
注意还少了一项电器属性的值啊?别急定义在dts中,0x17059
就刚好是电器属性的值。
5.2.1 client如何使用pinctrl
前面讲过client包含device结构体,每个device结构体里都有一个dev_pin_info
结构体,用来保存设备的pinctrl信息。platform_device
匹配driver会执行probe,probe前会进行pinctrl处理,处理函数为pinctrl_bind_pins
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| platform_device_register platform_device_add device_add bus_probe_device; device_initial_probe __device_attach bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
__device_attach_driver driver_match_device(drv, dev); drv->bus->match(dev, drv) driver_probe_device really_probe pinctrl_bind_pins drv->probe
|
5.2.1.1 pinctrl_bind_pins过程
- 构造
pinctrl
- 通过
pinctrl_ops.dt_node_to_map
将设备树节点转换成一系列pinctrl_map
pinctrl_map
转换成pinctrl_setting
,放入settings链表,记录在pinctrl_state
中
- 选择
state
,遍历settings链表,进行pinctrl的mux和config
函数调用过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| pinctrl_bind_pins devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); devm_pinctrl_get(dev); pinctrl_get create_pinctrl(dev); p = kzalloc(sizeof(*p), GFP_KERNEL); pinctrl_dt_to_map(p); for_each_maps(maps_node, i, map) { ret = add_setting(p, map); }
|
节点转换为pinctrl_map过程:
点击查看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| pinctrl_dt_to_map(p); for (state = 0; ; state++) { propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); prop = of_find_property(np, propname, &size); list = prop->value; size /= sizeof(*list); for (config = 0; config < size; config++) { np_config = of_find_node_by_phandle(phandle); dt_to_map_one_config(p, statename, np_config); ops->dt_node_to_map() dt_remember_or_free_map pinctrl_register_map list_add_tail } }
mc_pctrl_dt_node_to_map() for_each_child_of_node(np_config, np) { mc_pctrl_dt_subnode_to_map pins = of_find_property(node, "pinmux", NULL); num_pins = pins->length / sizeof(u32); for (i = 0; i < num_pins; i++) { of_property_read_u32_index(node, "pinmux",i, &pinfunc); pin = MC_GET_PIN_NO(pinfunc); func = MC_GET_PIN_FUNC(pinfunc); mc_pctrl_dt_node_to_map_func(); (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; (*map)[*num_maps].data.mux.group = grp->name; (*map)[*num_maps].data.mux.function = mc_gpio_functions[fnum]; (*num_maps)++; if (has_config) { pinctrl_utils_add_map_configs (*map)[*num_maps].type = type; (*map)[*num_maps].data.configs.group_or_pin = group; (*map)[*num_maps].data.configs.configs = dup_configs; (*map)[*num_maps].data.configs.num_configs = num_configs; (*num_maps)++; } } }
|
pinctrl_map转换为pinctrl_setting过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| for_each_maps() { add_setting(); find_state(); if (!state) create_state(p, map->name); list_add_tail(&state->node, &p->states); setting->type = map->type; setting->dev_name = map->dev_name; switch (map->type) { case PIN_MAP_TYPE_MUX_GROUP: pinmux_map_to_setting(map, setting); pinmux_func_name_to_selector(pctldev, map->data.mux.function); setting->data.mux.func = ret; ret = pinctrl_get_group_selector(pctldev, group); setting->data.mux.group = ret; case PIN_MAP_TYPE_CONFIGS_PIN: case PIN_MAP_TYPE_CONFIGS_GROUP: pinconf_map_to_setting(map, setting); setting->data.configs.group_or_pin = pin; setting->data.configs.num_configs = map->data.configs.num_configs; setting->data.configs.configs = map->data.configs.configs; } list_add_tail(&setting->node, &state->settings); }
|
pin脚的复用和配置过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| pinctrl_bind_pins pinctrl_lookup_state pinctrl_select_state pinctrl_commit_state list_for_each_entry(setting, &state->settings, node) { switch (setting->type) { case PIN_MAP_TYPE_MUX_GROUP: pinmux_enable_setting(setting); ops->set_mux(...); case PIN_MAP_TYPE_CONFIGS_PIN: case PIN_MAP_TYPE_CONFIGS_GROUP: pinconf_apply_setting(setting); ops->pin_config_group_set(...); }
|