1.参考文档
a. 内核 DocumentationdevicetreeindingsPinctrl 目录下: Pinctrl-bindings.txt b. 内核 Documentationgpio 目录下: Pinctrl-bindings.txt c. 内核 Documentationdevicetreeindingsgpio 目录下: gpio.txt
2.Pinctrl子系统概念
linux下的pintcrl和gpio子系统就类似于ST的“BSP库”。引入“设备树”之后,使用一个外设时,对于pin引脚的初始化和管理,只需通过设备树描述即可,然后由pin子系统管理;对于gpio则由gpio子系统管理。
因此,与CPU引脚“关联”的设备驱动,最终都会调用pincrtl和gpio子系统。二者是设备驱动的基础,这二者也是一个设备驱动。
CPU的gpio引脚除了的方向、速度、上下拉、驱动能力等基本的电气特性外,一般会包括复用功能,即该引脚既可以作为普通gpio,还可能复位为i2c引脚、uart引脚等。如果采用直接配置寄存器的方式进行驱动开发,会非常繁琐,每更改一个功能就得重新翻阅手册配一遍寄存器,另一方面还可能存在“冲突”问题,比如该引脚已被复用为i2c在使用,但被驱动工程师忽略了,再去使用该gpio时会导致未知预期的问题。引入pintctrl子系统就可以解决诸如此类问题,结合设备树的使用,只需把pin信息在设备树描述清楚,即由pinctrl子系统介入管理。
从设备树开始学习 Pintrl 会比较容易。
主要参考文档是:内核 Documentationdevicetreeindingspinctrlpinctrl-bindings.txt。
这会涉及 2 个对象:pin controller、client device。
前者提供服务:可以用它来复用引脚、配置引脚。
后者使用服务:声明自己要使用哪些引脚的哪些功能,怎么配置它们。
2.1 pin controller
在芯片手册里你找不到 pin controller,它是一个软件上的概念,你可以认为它对应IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)。
注意,pin controller 和 GPIO Controller 不是一回事,前者控制的引脚可用于 GPIO 功能、I2C 功能;后者只是把引脚配置为输出、输出等简单的功能。
2.2 client device
“客户设备”,谁的客户?Pinctrl 系统的客户,那就是使用 Pinctrl 系统的设备,使用引脚的设备。它在设备树里会被定义为一个节点,在节点里声明要用哪些引脚。
下面这个图就可以把几个重要概念理清楚: 上图中,左边是 pincontroller 节点,右边是 client device 节点:
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 子系统会自动根据上述信息把所用引脚配置为高电平
groups 和 function:
一个设备会用到一个或多个引脚,这些引脚就可以归为一组(group);
这些引脚可以复用为某个功能:function。
当然:一个设备可以用到多能引脚,比如 A1、A2 两组引脚,A1 组复用为 F1 功能,A2组复用为 F2 功能。
Generic pin multiplexing node 和 Generic pin configuration node:
在上图左边的 pin controller 节点中,有子节点或孙节点,它们是给 client device 使用的。
可以用来描述复用信息:哪组(group)引脚复用为哪个功能(function);
可以用来描述配置信息:哪组(group)引脚配置为哪个设置功能(setting),比如上拉、下拉等。
注意:pin controller 节点的格式,没有统一的标准!!!!每家芯片都不一样
3.GPIO系统
pinctrl子系统主要是管理pin的电气属性和复用功能,而gpio子系统则是管理gpio的申请释放、控制输入输出、io中断等功能。gpio子系统屏蔽了gpio相关寄存器的配置过程,换而提供了常用的接口函数给驱动工程师使用,方便gpio相关的驱动开发。
gpio子系统功能
- 对于驱动层,屏蔽gpio寄存器配置细节,提供统一gpio操作接口
- 对于BSP层,统一框架,方便不同CPU接入,只需更换pinctrl子系统的驱动
3.1 GPIO子系统常用函数
gpio子系统对于驱动层的API位于“/kernel/include/linux/gpio.h”中。
(1)检查gpio是否可用
int gpio_is_valid(int number); number: gpio序号 返回: 可用返回true,不可用返回false
(2)申请使用一个gpio
使用一个gpio前,必须向内核申请该gpio。 int gpio_request(unsigned gpio, const char *label) gpio: 待申请gpio序号 label:gpio命名 返回: 成功返回0,失败返回负数
(3)释放已申请gpio
如果不使用该gpio,则需要释放,否则其他模块申请不到该gpio序号。 int gpio_free(unsigned gpio) gpio:待释放gpio序号
返回:成功返回0,失败返回负数
(4)设置gpio输入模式
int gpio_direction_input(unsigned gpio) gpio:待设置gpio序号 返回:成功返回0,失败返回负数
(5)设置gpio输出模式
void gpio_set_value(unsigned gpio, int value) gpio:待设置gpio序号 value:默认输出状态
(6)读取 gpio状态
int gpio_get_value(unsigned int gpio) gpio:待读取gpio序号 返回:成功返回gpio状态(1/0),失败返回负数
(7)设置 gpio状态
void gpio_set_value(unsigned int gpio, int value) gpio:待设置gpio序号 value:待设置值
(8)中断号映射
int gpio_to_irq(unsigned gpio) gpio:待设置gpio序号 返回:成功返回中断号,失败返回负数
如果使用该 GPIO 时,不会动态的切换输入输出,建议在开始时就设置好 GPIO 输出方向,后面拉高拉低时使用 gpio_set_value()接口,而不建议使用gpio_direction_output(), 因为 gpio_direction_output 接口里面有 mutex 锁,对中断上下文调用会有错误异常,且相比 gpio_set_value,gpio_direction_output 所做事情更多,浪费。