GPIO和Pinctrl子系统的使用
问题引出:
在实际开发中我们并不会直接操作寄存器去写驱动。太低效了,这样的话我们就变成了“寄存器工程师了”,即使是做单片机的都不执着于裸写寄存器。
Linux下针对引脚有2个重要的子系统:GPIO,Pinctrl
pinctrl子系统重要概念
在平时芯片的引脚几百个,在使用GPIO功能是,让一个引脚一个引脚去找对应的寄存器很麻烦。所以要把引脚的复用,配置抽出来,做成Pinctrl子系统,给GPIO,I2C等模块使用。
BSP工程师所做的事:
等BSP工程师在GPIO子系统,Pinctl子系统中把自家的芯片的支持加进去后,我们就可以非常方便的使用这些引脚了,在图上看GPIO和Pinctrl是分开的,但是在实际上并没有单独的IOMUX模块,引脚的复用,配置就是在GPIO模块内部实现的。在硬件上GPIO和Pinctrl是如此紧密相关。
重要概念
这会涉及 2 个对象:pin controller、client device。
前者提供服务:可以用它来复用引脚、配置引脚。
后者使用服务:声明自己要使用哪些引脚的哪些功能,怎么配置它们。
pin controller:
在芯片手册里你找不到 pin controller,它是一个软件上的概念,你可以认为它对应 IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)。注意,pin controller 和 GPIO Controller 不是一回事,前者控制的引脚可用于 GPIO 功能、I2C 功能;后者只是把引脚配置为输入、输出等简单的功能。即先用 pin controller 把引脚配置为 GPIO,再用 GPIO Controler 把引脚配置为输入或输出。
client device
是使用 Pinctrl 系统的设备,使用引脚的设备。它在设备树里会被定义为一个节点,在节点里声明要用哪些引脚。
代码中怎么引用pinctrl
GPIO子系统重要概念
要操作 GPIO 引脚,先把所用引脚配置为 GPIO 功能,这通过Pinctrl 子系统来实现。然后就可以根据设置引脚方向(输入还是输出)、读值──获得电平状态,写值──输出高低电平。
以前我们通过寄存器来操作 GPIO 引脚,即使 LED 驱动程序,对于不同的板子它的代码也完全不同。
当 BSP 工程师实现了 GPIO 子系统后,我们就可以:
⚫ 在设备树里指定 GPIO 引脚
⚫ 在驱动代码中:使用 GPIO 子系统的标准函数获得 GPIO、设置 GPIO 方向、读取/设置 GPIO 值。这样的驱动代码,将是单板无关的。
这样就不会因为单板的不同而重新设置硬件寄存器。
在设备树中指定引脚
在几乎所有 ARM 芯片中,GPIO 都分为几组,每组中有若干个引脚。所以在
使用 GPIO 子系统之前,就要先确定:它是哪组的?组里的哪一个?
在设备树中,“GPIO 组”就是一个 GPIO Controller,这通常都由芯片厂家
设置好。我们要做的是找到它名字,比如“gpio1”,然后指定要用它里面的哪个
引脚,比如<&gpio1 0>。
我们暂时只需要关心里面的这 2 个属性:
gpio-controller;
#gpio-cells = <2>;
⚫ “gpio-controller”表示这个节点是一个 GPIO Controller,它下面有很多引脚。
⚫ “#gpio-cells = <2>”表示这个控制器下每一个引脚要用 2 个 32 位的数(cell)来描述。
为什么要用 2 个数?其实使用多个 cell 来描述一个引脚,这是 GPIO
Controller 自己决定的。比如可以用其中一个 cell 来表示那是哪一个引脚,用另一个 cell 来表示它是高电平有效还是低电平有效,甚至还可以用更多的cell 来示其他特性。
普遍的用法是,用第 1 个 cell 来表示哪一个引脚,用第 2 个 cell 来表示有效电平:
GPIO_ACTIVE_HIGH : 高电平有效
GPIO_ACTIVE_LOW : 低电平有效
定义 GPIO Controller 是芯片厂家的事,我们怎么引用某个引脚呢?在自己的设备节点中使用属性"[-]gpios",示例如下: