GPIO子系统框架分析

1、作用:

在裸机中,我们都是对寄存器直接进行操作的。而在Linux中,对于驱动开发者来说,对于已将引脚复用为GPIO的引脚,我们可以借助gpio子系统提供的函数,间接的来实现裸机中,对寄存器的操作。
简单的理解,GPIO子系统就是用来GPIO进行初始化,并提供相应的API函数,供驱动开发者使用,无需驱动开发者再去设置GPIO。
驱动开发人员要做的就是:在设备书下,创建节点,在节点中添加pinctrl信息(引脚的复用和电气属性信息)和gpio属性信息。然后,就可以通过使用gpio子系统提供的函数进行对GPIO操作了。

配置示例

pinctrl子系统部分:

pinctrl_test: testgrp{
    
    
fsl, pins = <
MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config >;
}    

gpio子系统部分:

test{
    
    
	pinctrl - names = "default";
	pinctrl - 0 = <&pinctrl_test>;
//表示 GPIO1 组的第 1 号 IO,默认低电平有效
	gpio = <&gpio1 1 GPIO_ACTIVE_LOW>;
}  

2、GPIO子系统框架

2.1框架的整体描述:

GPIO子系统框架大致分为三层:
1)、GPIO硬件层:由多个GPIO_Control组成,每个Controller管理一组GPIO(这个组的应用,就是在设备树中配置gpio的时候,有这样一项(这个项中,第一个参数就是组;第二个参数是哪一个引脚):
2)、GPIO硬件驱动层(内核空间):
linux内核中的gpio子系统是铜过gpiolib来实现的。
Gpiolib 其实就是围绕几个抽象的结构体在做文章:
a、gpio_desc: gpio_desc结构体 是抽象出来的每一个gpio引脚的描述符(这部分,BSP工程师已经做好)
b、gpio_chip: gpio_chip 结构体 是抽象出来的一组GPIO 控制器。
此结构是为了抽象 GPIO 的所有操作,同时适配不同芯片的一个 common 的结构,所以,这个结构是要开出去给其他芯片进行特定的操作赋值的,比如你是 IMX6ULL的芯片,那么你需要实现你的这些 gpio_chip 的内容。
c、gpio_device:是抽象出来的一个bank。
其实,gpio注册(gpiochip_add)本质就是注册到gpio_desc这个结构体数组里,使用gpiochip_add函数。
在这里插入图片描述
图的来源:

https://blog.csdn.net/lhl_blog/article/details/108179945?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161613142216780266296052%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161613142216780266296052&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-1-108179945.pc_search_result_hbase_insert&utm_term=gpio%E5%AD%90%E7%B3%BB%E7%BB%9F%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90

3)、抽象驱动框架层(用户空间):
框架的实现流程:
我们在pinctrl子系统里面,首先设置引脚复用,并设置电气属性,也就是赋予引脚功能。但是,这个引脚是给哪个设备用的,这需要通过gpio子系统来设置。我们在设备树中,找到对应设备的节点,将设备节点下,添加设备的pinctrl和gpio信息。eg:配置gpio为SD卡的检测引脚。我们就需要在SD卡的设备节点下面添加pinctrl和gpio信息。这样SD卡就会找到对应引脚,通过引脚的高低电平,即可判断出是否有SD卡插入。
另外,每个对应GPIO都属于指定的组。
eg:

gpio = <&gpio1 1 GPIO_ACTIVE_LOW>;从这个就可以看出,gpio1的1号io属于gpio1组。

每一组的信息,在设备数中也有对应的节点来表示。eg:gpio1节点。这个节点下,重点就是这组gpio的外设基地址以及兼容属性。
eg:

gpio1: gpio@0209c000 //表示GPIO1控制器的寄存器基地址是0X0209C000{
    
    
//用来表示兼容属性。在内核中,要想使用这组Gpio下的节点,兼容属性值必须是下面两种中的一种。即在支持该板子的内核中的GPIO驱动文件中,一定可以找到至少一个属性值。

eg: 这个叫做匹配表,用来实现将内核中驱动文件和设备树中的节点进行匹配(根据compatible属性)。

static const struct of_device_id mxc_gpio_dt_ids[] = {
    
    
	{
    
     .compatible = "fsl,imx1-gpio", .data =
	&mxc_gpio_devtype[IMX1_GPIO], },
	{
    
     .compatible = "fsl,imx21-gpio", .data =
	&mxc_gpio_devtype[IMX21_GPIO], },
	{
    
     .compatible = "fsl,imx31-gpio", .data =
	&mxc_gpio_devtype[IMX31_GPIO], },
	{
    
     .compatible = "fsl,imx35-gpio", .data =
	&mxc_gpio_devtype[IMX35_GPIO], },
	{
    
     /* sentinel */ }
};
	compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
//一组内的GPIO,共用一组寄存器。这句标表示GPIO1 控制器的寄存器基地址为 0X0209C000
	reg = <0x0209c000 0x4000>;  
	interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
	<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
	gpio - controller;//表示GPIO1节点是一个GPIO控制器。
	#gpio - cells = <2>;
	interrupt - controller;
	#interrupt - cells = <2>;
};

在内核的GPIO驱动文件中,当设备树种的节点的compatible与驱动文件中匹配表中的compatible匹配以后,就会执行驱动的入口函数probe_xxx。在这个函数中我们要做的是:
1)、获取设备树的节点:

struct device_node *np = pdev->dev.of_node;

2)、获取对应gpio所使用的的寄存器组(也就是gpio所使用的所有寄存器的偏移地址)。

static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
    
    
	dr_reg = 0x00,
	gdir_reg = 0x04,
	psr_reg = 0x08,
	imr_reg = 0x14,
	low_level = 0x00,
	high_level = 0x01,
	rise_edge = 0x02,
	fall_edge = 0x03,
};

3)、获取设备树节点中的reg 属性值值,也就是寄存器的基地址。
有了基地址信息,加上面得到的各寄存器偏移地址,这样,内核就可以对寄存器组(GPIO1)中所有的寄存器进行访问了。
4)、初始化gpio_chip结构体。
在对gpio_chip结构体gpio_chip结构体初始化过程中,会写入开发板(I.MX6ULL)有关 GPIO的寄存器信息(基地址。。。。。。)。同时,gpio_chip结构体中,还有GPIO的操作函数。
5)、调用函数gpiochip_add向Linux内核注册gpio_chip
完成住粗以后,我们就可以在驱动中,使用gpiolib提供的API函数对寄存器就行操作了。
在这里插入图片描述
图的来源:
https://xuesong.blog.csdn.net/article/details/108909011
扩展:
我使用的内核是4.1.15。在每个gpio_desc结构体中,有一个 struct gpio_chip *chip;成员变量,用来指定这个GPIO所属的控制器,也就是属于哪个chip。在每个gpio_chip结构体中,有一个gpio_device成员变量(每一个gpio_chip内均包括该gpio_chip所支持的所有gpio,即gpio_desc。这些信息,在其成员变量gpio_device中)。
在内核4.4中:
看起来 gpio_chip 的成员变量是gpio_device(同1.1.15)和 gpio_desc的成员变量也是gpio_device,而不是gpio_chip(和1.1.15)。 应该是包含关系,但是 Kernel 中并没有直接将其两个结构联系上,而是通过另外一个结构将其联系在一起,这个结构就是 gpio_device。
gpio_device结构体包含gpio控制器所有的gpio对应的gpio_desc等信息。系统中所有gpio_device都会插入到一个全局链表gpio_devices里,以后通过gpio号,可以在该链表上找到对应的gpio_desc以及所属gpio_chip。

猜你喜欢

转载自blog.csdn.net/cainiaofu/article/details/115004841
今日推荐