Linux内核的GPIO子系统是用于管理和控制通用输入输出(GPIO)引脚的软件框架。它提供了一套统一的接口和机制,使开发者能够方便地对GPIO进行配置、读写和中断处理。
主要组件:
GPIO框架:提供了一套API和数据结构,用于在驱动程序中注册和操作GPIO引脚。这包括GPIO的请求、释放、配置以及读写等功能。
GPIO控制器驱动:每个具体的硬件平台都需要编写相应的GPIO控制器驱动程序,负责与硬件进行交互,并向上层提供统一的接口。它们通过设备树描述文件来与硬件平台进行绑定。
GPIO消费者设备:用户空间或其他内核模块中可以通过GPIO消费者设备来请求使用特定的GPIO资源,并进行配置、读写等操作。常见的消费者设备有LED驱动、按键输入设备等。
中断处理:GPIO子系统支持对GPIO引脚触发中断事件进行处理。当某个GPIO引脚状态发生变化时,相关的中断处理程序会被调用,开发者可以编写自己的中断处理函数来响应相应事件。
通过这些组件,开发者可以使用GPIO子系统来管理和控制系统中的GPIO引脚。他们可以请求、配置和读写GPIO资源,以实现与外部设备的交互,如控制LED灯的亮度、检测按键输入等。
作用:
抽象硬件差异:不同硬件平台上的GPIO引脚数量、编号、电压级别等特性可能各不相同。GPIO子系统提供了一个标准化的接口,使得开发者可以在不关心底层硬件细节的情况下编写通用代码。
灵活配置:通过GPIO子系统,用户可以动态地配置GPIO引脚为输入或输出模式,并设置其电平状态、中断触发方式等。这样就可以实现对外部设备进行读取和控制操作。
中断支持:GPIO子系统允许用户注册回调函数,以响应GPIO引脚状态变化所产生的中断事件。这种机制在需要及时处理外部事件时非常有用,例如按键输入、传感器触发等。
用户空间访问:通过文件系统接口(sysfs或者更现代化的字符设备接口),用户空间程序可以直接与GPIO子系统进行交互,读取和设置GPIO引脚的状态。这使得用户可以使用各种编程语言和工具进行GPIO的应用开发。
嵌入式工程师总是要处理各种各样的target board,每个target board上的GPIO总是存在不同。
对于ARM的嵌入式硬件平台,SOC本身可以提供大量的IO port,SOC上的GPIO controller是通过SOC的总线(AMBA)连接到CPU的。
对于嵌入式系统而言,除了SOC的IO port,一些外设芯片也可能会提供IO port,例如:
(1)有些key controller芯片、codec或者PMU的芯片会提供I/O port
(2)有些专用的IO expander芯片可以扩展16个或者32个GPIO
从硬件角度看,这些IO和SOC提供的那些IO完全不同,CPU和IO expander是通过I2C(也有可能是SPI等其他类型的bus)连接的,在这种情况下,访问这些SOC之外的GPIO需要I2C的操作,而控制SOC上的GPIO只需要写寄存器的操作。
不要小看这个不同,写一个SOC memory map的寄存器非常快,但是通过I2C来操作IO就不是那么快了,甚至,如果总线繁忙有可能阻塞当前进程,这种情况下,内核同步机制必须有所区别(如果操作GPIO可能导致sleep,那么同步机制不能采用spinlock)。
SOC片内的GPIO controller和SOC片外的IO expander的访问当然不一样,不过,即便都是SOC片内的GPIO controller,不同的ARM芯片,其访问方式也不完全相同,例如:有些SOC的GPIO controller会提供一个寄存器来控制输出电平。向寄存器写1就是set high,向寄存器写0就是set low。但是有些SOC的GPIO controller会提供两个寄存器来控制输出电平。向其中一个寄存器写一就是set high,向另外一个寄存器写一就是set low。
即便是使用了同样的硬件(例如都使用同样的某款SOC),不同硬件系统上GPIO的配置不同。
在一个系统上配置为输入,在另外的系统上可能配置为输出。
这些特性包括:
(1)是否能触发中断。对一个SOC而言,并非所有的IO port都支持中断功能,可能某些处理器只有一两组GPIO有中断功能。
(2)如果能够触发中断,那么该GPIO是否能够将CPU从sleep状态唤醒
(3)有些有软件可控的上拉或者下拉电阻的特性,有的GPIO不支持这种特性。在设定为输入的时候,有的GPIO可以设定debouce的算法,有的则不可以。
5、多功能复用
有的GPIO就是单纯的作为一个GPIO出现,有些GPIO有其他的复用的功能。例如IO expander上的GPIO只能是GPIO,但是SOC上的某个GPIO除了做普通的IO pin脚,还可以是SPI上clock信号线。
ARM based SOC的datasheet中总有一个章节叫做GPIO controller(或者I/O ports)的章节来描述如何配置、使用SOC的引脚。
虽然GPIO controller的硬件描述中充满了大量的寄存器的描述,但是这些寄存器的功能大概分成下面三个类别:
1、有些硬件逻辑是和IO port本身的功能设定相关的,我们称这个HW block为pin controller。软件通过设定pin controller这个硬件单元的寄存器可以实现:
(1)引脚功能配置。例如该I/O pin是一个普通的GPIO还是一些特殊功能引脚(例如memeory bank上CS信号)。
(2)引脚特性配置。例如pull-up/down电阻的设定,drive-strength的设定等。
2、如果一组GPIO被配置成SPI,那么这些pin脚被连接到了SPI controller,如果配置成GPIO,那么控制这些引脚的就是GPIO controller。通过访问GPIO controller的寄存器,软件可以:
(1)配置GPIO的方向
(2)如果是输出,可以配置high level或者low level
(3)如果是输入,可以获取GPIO引脚上的电平状态
3、如果一组gpio有中断控制器的功能,虽然控制寄存器在datasheet中的I/O ports章节描述,但是实际上这些GPIO已经被组织成了一个interrupt controller的硬件block,它更像是一个GPIO type的中断控制器,通过访问GPIO type的中断控制器的寄存器,软件可以:
(1)中断的enable和disable(mask和unmask)
(2)触发方式
(3)中断状态清除
传统的GPIO driver是负责上面三大类的控制,而新的linux kernel中的GPIO subsystem则用三个软件模块来对应上面三类硬件功能:
(1)pin control subsystem。驱动pin controller硬件的软件子系统。
(2)GPIO subsystem。驱动GPIO controller硬件的软件子系统。
(3)GPIO interrupt chip driver。这个模块是作为一个interrupt subsystem中的一个底层硬件驱动模块存在的。
本文主要描述前两个软件模块,具体GPIO interrupt chip driver以及interrupt subsystem请参考本站其他相关文档。
下图描述了pin control subsystem的模块图:
底层的pin controller driver是硬件相关的模组,初始化的时候会向pin control core模块注册pin control设备(通过pinctrl_register这个bootom level interface)。
pin control core模块是一个硬件无关模块,它抽象了所有pin controller的硬件特性,仅仅从用户(各个driver就是pin control subsystem的用户)角度给出了top level的接口函数,这样,各个driver不需要关注pin controller的底层硬件相关的内容。