详解Pinctrl子系统
在把前面的同步互斥机制讲完后,今天我们来开始去学习子系统啦,我们先来看看第一个子系统,pinctrl子系统:
无论是哪种芯片,都有类似下图的结构:
想要pinA、B用于GPIO,需要设置IOMUX让它们连接到GPIO模块;要想让pinA、B用于I2C,需要设置IOMUX让它们连接到I2C模块,这里GPIO、I2C应该是并列的关系,它们能够使用之前,需要设置复用关系IOMUX,有时还要配置引脚,比如上拉、下拉、开漏等等。
现在芯片一般动辄几百个引脚,在使用到GPIO、I2C等功能时,若一个引脚一个引脚去找对应的寄存器进行配置非常浪费时间和精力,所以内核引入了Pinctrl子系统,把引脚的复用和配置抽象出来,只需要芯片厂商把自家芯片的支持进去,就可以很方便的配置引脚。
Pinctrl:Pin Controller,顾名思义,就是用来控制引脚的:
- 引脚枚举与命名(Enumerating and naming)
- 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
- 引脚配置(Configuration):比如上拉、下拉、开漏、驱动强度等
Pinctrl重要概念:
从设备树开始学习Pinctrl会比较容易。参考内核Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt,Pinctrl子系统涉及2个对象:Pin controller devices、Pinctrl client devices。
- Pin controller devices:提供服务,可以用它来复用引脚、配置引脚,是一个软件上的概念。
注意:Pin controller和GPIO Controller不同,前者控制的引脚可用于GPIO功能、I2C功能;后者只是把引脚配置为输入、输出等简单的功能。两者的关系是先用Pin controller把引脚配置为GPIO,再用GPIO Controler把引脚配置为输入或输出。 - Pinctrl client devices:使用服务,Pinctrl系统的客户,即使用Pinctrl系统的设备。声明自己要使用哪些引脚的哪些功能,怎么配置它们。
在设备树中,上述两个对象被定义成两个节点,如下图所示。左边是Pin controller节点,右边是client device节点:
上图中,左边是pin controller节点,右边是client device节点:
a. 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节点中。
当这个设备处于default状态时,pinctrl子系统会自动根据上述信息把所用引脚复用为uart0功能。
当这这个设备处于sleep状态时,pinctrl子系统会自动根据上述信息把所用引脚配置为高电平。
b. groups和function:
一个设备会用到一个或多个引脚,这些引脚就可以归为一组(group);
这些引脚可以复用为某个功能:function。
当然:一个设备可以用到多组引脚,比如A1、A2两组引脚,A1组复用为F1功能,A2组复用为F2功能。
c. Generic pin multiplexing node和Generic pin configuration node
在上图左边的pin controller节点中,有子节点或孙节点,它们是给client device使用的。
可以用来描述复用信息:哪组(group)引脚复用为哪个功能(function);
可以用来描述配置信息:哪组(group)引脚配置为哪个设置功能(setting),比如上拉、下拉等。
注意:pin controller节点的格式,没有统一的标准!!!!每家芯片都不一样。
甚至上面的group、function关键字也不一定有,但是概念是有的。
这是由于怎么去解析Pincontroller节点下的子节点完全是有芯片厂商决定,内核Pinctrl驱动框架只定义了解析节点的接口而已,而Client端是由内核代码统一管理的。
Pinctrl子系统主要数据结构:
实际上的:
pincontroller的数据结构:
记住pinctrl的三大作用,有助于理解所涉及的数据结构:
-
引脚枚举与命名(Enumerating and naming)
-
引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
-
引脚配置(Configuration):比如上拉、下来、open drain、驱动强度等
pinctrl_desc和pinctrl_dev:
1. 结构体引入
pincontroller虽然是一个软件的概念,但是它背后是有硬件支持的,所以可以使用一个结构体来表示它:pinctrl_dev。
怎么构造出pinctrl_dev?我们只需要描述它:提供一个pinctrl_desc,然后调用pinctrl_register就可以:
怎么使用pinctrl_desc、pinctrl_dev来描述一个pin controller?这两个结构体定义如下:
pinctrl_desc示例如下:
- pinctrl_pin_desc:描述引脚
- pinctrl_ops:引脚控制操作,用来获取某组引脚,解析设备树节点创建映射
- pinmux_ops:复用操作
- pinconf_ops:配置操作
2. 作用1:描述、获取、解析引脚
使用结构体pinctrl_pin_desc来描述一个引脚,一个pin controller有多个引脚:
使用pinctrl_ops来操作引脚,主要功能如下:
- 来取出某组的引脚:get_groups_count、get_group_pins
- 处理设备树中pin controller中的某个节点创建映射:dt_node_to_map,把device_node转换为一系列的pinctrl_map
5. 使用pinctrl_desc注册得到pinctrl_dev
调用devm_pinctrl_register或pinctrl_register,就可以根据pinctrl_desc构造出pinctrl_dev,并且把pinctrl_dev放入链表:
client的数据结构:
在设备树中,使用pinctrl时格式如下:
device结构体:
设备节点要么被转换为platform_device,或者其他结构体(比如i2c_client),但是里面都会有一个device结构体:
dev_pin_info、pinctrl、pinctrl_map和pinctrl_setting:
每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息:
pinctrl:
假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?
这需要通过设备树来确定:
-
分析设备树,找到pin controller
-
对于每个状态,比如default、init,去分析pin controller中的设备树节点
-
使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一系列的pinctrl_map
-
这些pinctrl_map放在pinctrl.dt_maps链表中
-
每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中
-
那么我们先讲到这里吧,完结,撒花(doge.)