下面的内容均在imx6平台上举例,这一次分析希望将整个GPIO子系统的所有细节整理清晰。
第一篇从gpiolib入手,后面的边分析边写.
开始之前给自己提几个问题
驱动开发中的GPIO API 究竟是怎么实现的?
GPIO的中断又是怎么实现的?
GPIO号和IRQ的号码怎么映射的?
1.芯片定义
我们在驱动程序中会用到gpio_request(x),这里的x便是gpio的编号,而GPIO通常会分组,在原理图经常会看见GPIO2_5类似的标识,通常我们先会翻阅一下datesheet来了解一下这块芯片的定义是如何。
在imx6芯片中的将GPIO分成了若干组,每组为32个管脚。
我们简单的阅读一下芯片手册:
IMX6的gpio控制结构:
iomux_block.jpg
我们的芯片会引出一堆引脚,而在芯片的内部会集成很多control,(我们称这些control为block)其中的引脚可以功能复用,比如说A1 A2脚支持I2C功能也支持GPIO功能,我可以将I2C1_control的管脚连接过来,或者将GPIO1_1的管脚连接上来,这个就是IOMUX的作用:提供PIN不同的功能切换。
所以我们在使用芯片之前会先根据主板的配置先将各个管脚配置为正确的功能的Pin.
寄存器:
Data register (GPIO_DR)
GPIO direction register (GPIO_GDIR)
Pad sample register (GPIO_PSR)
Interrupt control registers (GPIO_ICR1, GPIO_ICR2)
Interrupt mask register (GPIO_IMR)
Interrupt status register (GPIO_ISR)
GPIO edge select register (GPIO_EDEG_SEL )
简单介绍一下
DR 当作为输出时控制管脚高低电平
GDIR 控制管脚作为输入还是输出
PSR 作为输出时获取管脚的高低电平
ICR1 ICR2 是配置中断的触发方式 高/低电平触发 上升/下降沿 触发
IMR 中断屏蔽寄存器
ISR 中断状态寄存器 哪个管脚触发了中断
EDEG_SEL 边缘触发模式(上/下沿都触发)
GPIO有7组,每组32个,最后一组14个
2.设备树
我们了解了一下内部的结构,接着就要开始看代码了,在看代码之前看看设备树,才能定位到代码在哪里。
这里只列出一个gpio-controller
aliases {
ethernet0 = &fec;
can0 = &can1;
can1 = &can2;
gpio0 = &gpio1;
gpio1 = &gpio2;
.....
}
gpio1: gpio@0209c000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
<0 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
我们通过compatible的值可以找到对应的驱动代码路径 driver/gpio/gpio-mxc.c
GPIO1控制寄存器的地址是0x0209c000,长度0x4000,使用66和67号中断。
这里疑惑的地方是为什只用了两个中断号,而不是32个?
我们知道一个GPIO控制是通过总线连接到CPU,中断连接到中断控制器(GIC),那么中断线太多就以为着GIC的数量也需要增加,如果GPIO控制器内部进行判断是哪个管脚触发的那么就可以避免GIC的数量。
而我们的芯片内部只有一个GIC.
IRQ
Source
Interrupt Description
32
IOMUXC
General Purpose Register 1 from IOMUXC. Used to notify cores on exception condition whileboot.
33
DAP
Debug Access Port interrupt request
98
GPIO1
Combined interrupt indication for GPIO1 signals 0 - 15.
99
GPIO1
Combined interrupt indication for GPIO1 signals 16 - 31.
这里中断号的来源是芯片手册,因为GIC的连接是这样的,这里0-31是内部使用,32开始用作外设。
驱动中用编号0直接开始编号。
地址参考寄存器MAP
Absolute
address
Register name Widt (bits)
Access
Reset value
209_C000
GPIO data register (GPIO1_DR)
32
R/W
0000_0000h
209_C004
GPIO direction register (GPIO1_GDIR)
32
R/W
0000_0000h
209_C008
GPIO pad status register (GPIO1_PSR)
32
R
0000_0000h
209_C00C
GPIO interrupt configuration register1 (GPIO1_ICR1)
32
R/W
0000_0000h
209_C010
GPIO interrupt configuration register2 (GPIO1_ICR2)
32
R/W
0000_0000h
209_C014
GPIO interrupt mask register (GPIO1_IMR)
32
R/W
0000_0000h
209_C018
GPIO interrupt status register (GPIO1_ISR)
32
w1c
0000_0000h
209_C018
GPIO interrupt status register (GPIO1_ISR)
32
w1c
0000_0000h
209_C01C
GPIO edge select register (GPIO1_EDGE_SEL)
32
R/W
0000_0000h
20A_0000
GPIO data register (GPIO2_DR)
32
R/W
0000_0000h
3.代码
从设备树中我们知道了代码的路径位置,我们先看看gpio这个目录的Makefile:
obj-$(CONFIG_GPIO_DEVRES) += devres.o
obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
.....(省略一些无关紧要的内容)
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
这里分三类:
gpiolib的代码
gpio-generic代码
gpio特定平台的代码
代码的结构:
GPIOLIB 调用 GPIO_GENRIC 调用 GPIO_MXC
接着我们开始分析代码:
代码主要讲了初始化GPIO控制器和GPIO的中断控制器,这里先分析gpio控制器和如何与gpiolib联系
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 */ }
};
enum mxc_gpio_hwtype {
IMX1_GPIO, /* runs on i.mx1