i.MX6ULL驱动开发 | 07 -gpio子系统
Posted Mculover666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了i.MX6ULL驱动开发 | 07 -gpio子系统相关的知识,希望对你有一定的参考价值。
一、gpio子系统
在上一篇文章(i.MX6ULL驱动开发 | 06 - pinctrl子系统)中,以 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 这个引脚为例,讲述了如何在设备树中描述该引脚的复用功能和电气属性,并走马观花的了解了pinctrl子系统如何根据设备树的描述去设置对应的底层寄存器。
pinctrl子系统用来设置某个引脚复用为GPIO,本节则讲述如何通过gpio子系统来使用某个gpio引脚,比如控制gpio引脚输出高低电平。
对于使用者来说,只需要在设备树里面设置好某个GPIO的相关属性即可,其余的工作均由gpio子系统来完成,gpio子系统源码目录为:drivers/gpio。
二、设备树中如何描述gpio的使用
1. 绑定文档
在绑定文档Documentation/devicetree/bindings/gpio/fsl-imx-gpio.txt
中。
Freescale i.MX/MXC GPIO controller需要的节点属性有:
- compatible : Should be “fsl,-gpio”
- reg : Address and length of the register set for the device
- interrupts :
Should be the port interrupt shared by all 32 pins, if one number.
If two numbers, the first one is the interrupt shared by low 16 pins and the second one is for high 16 pins. - gpio-controller : Marks the device node as a gpio controller.
- #gpio-cells : Should be two. The first cell is the pin number and the second cell is used to specify the gpio polarity:
0 = active high
1 = active low - interrupt-controller: Marks the device node as an interrupt controller.
- #interrupt-cells : Should be 2. The first cell is the GPIO number. The second cell bits[3:0] is used to specify trigger type and level flags:
1 = low-to-high edge triggered.
2 = high-to-low edge triggered.
4 = active high level-sensitive.
8 = active low level-sensitive.
示例:
gpio0: gpio@73f84000
compatible = "fsl,imx51-gpio", "fsl,imx35-gpio";
reg = <0x73f84000 0x4000>;
interrupts = <50 51>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
;
2. 实例分析
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19引脚是作为SD1外设的SD引脚使用,用来进行SD卡热插拔。
在imx6ull-atk-emmc.dts
设备树中的usdhc1节点中:
(1)pinctrl-xxx属性
pinctrl-0、pinctrl-1、pinctrl-2有三组,分别找到其引用的标号,找到该描述:
可以看到在三组描述中,引脚复用功能描述都是一样作为SD外设的引脚,但是引脚的电气属性却不一样,主要区分在于引脚的速度配置不同。
(2)cd-gpios属性
该属性的属性值中引用了 gpio1 标签,找到其描述,在imx6ull.dtsi
文件中:
对照绑定文档,可以看到每一项属性的意义:
- compatible:兼容性,“fsl,imx6ul-gpio”、"fsl,imx35-gpio"两个值
- reg:寄存器,从0x0209c000开始,长度为0x4000
- interrupts:中断绑定,有两个值,第一个值为gpio1低16位引脚共享中断,第二个值为gpio1高16位共享中断
- gpio-controller:标记该节点是一个gpio控制器
- #gpio-cells:应该是2,第一个单元是引脚号,第二个单元用来指明引脚极性
- interrupt-controller:标记该节点是一个中断控制器
- #interrupt-cells:应该是2,第一个单元是gpio号,第二个单元用来指明触发类型和电平标志
回到cd-gpios属性的值,在引用了 gpio1 标签之后,又跟了两个值:19 GPIO_ACTIVE_LOW
。
- 19:表示使用gpio外设的第19个引脚
- GPIO_ACTIVE_LOW:表示低电平有效
引脚极性GPIO_ACTIVE_LOW是一个宏,在include/linux/gpio/machine.h
头文件中定义:
enum gpio_lookup_flags
GPIO_ACTIVE_HIGH = (0 << 0),
GPIO_ACTIVE_LOW = (1 << 0),
GPIO_OPEN_DRAIN = (1 << 1),
GPIO_OPEN_SOURCE = (1 << 2),
;
三、驱动中对gpio的操作
1. 操作流程
- 从设备树获取到gpio编号
- 基于gpio编号,利用GPIO子系统对gpio进行操作
2. 如何获取设备树中的gpio属性信息
在include/linux/of_gpio.h
文件中声明并实现。
(1)获取设备树某个属性里面定义了几个gpio信息
/**
* of_gpio_named_count() - Count GPIOs for a device
* @np: device node to count GPIOs for
* @propname: property name containing gpio specifier(s)
*
* The function returns the count of GPIOs specified for a node.
* Note that the empty GPIO specifiers count too. Returns either
* Number of gpios defined in property,
* -EINVAL for an incorrectly formed gpios property, or
* -ENOENT for a missing gpios property
*
* Example:
* gpios = <0
* &gpio1 1 2
* 0
* &gpio2 3 4>;
*
* The above example defines four GPIOs, two of which are not specified.
* This function will return '4'
*/
static inline int of_gpio_named_count(struct device_node *np, const char* propname)
return of_count_phandle_with_args(np, propname, "#gpio-cells");
参数np表示设备节点,参数propname表示包含gpio信息的属性名称。
返回值表示统计到的gpio数量。
(2)统计"gpios"这个属性的gpio数量
/**
* of_gpio_count() - Count GPIOs for a device
* @np: device node to count GPIOs for
*
* Same as of_gpio_named_count, but hard coded to use the 'gpios' property
*/
static inline int of_gpio_count(struct device_node *np)
return of_gpio_named_count(np, "gpios");
(3)获取gpio引脚编号
/**
* of_get_named_gpio() - Get a GPIO number to use with GPIO API
* @np: device node to get GPIO from
* @propname: Name of property containing gpio specifier(s)
* @index: index of the GPIO
*
* Returns GPIO number to use with Linux generic GPIO API, or one of the errno
* value on the error condition.
*/
static inline int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
return of_get_named_gpio_flags(np, propname, index, NULL);
参数np表示设备节点,参数propname表示包含gpio信息的属性名,参数index表示gpio索引(一个属性值中可能包含多个gpio)。
返回值为获取到的gpio编号,否则为负。
3. gpio子系统API函数
gpio子系统向驱动开发者提供了统一的gpio操作API。
3.1. gpio申请与释放
在drivers/gpio/gpiolib-legacy.c
文件中实现。
(1)申请一个GPIO引脚
int gpio_request(unsigned gpio, const char *label);
参数意义如下:
- gpio:gpio引脚编号
- label:给gpio设置的名字
返回值如果为0则申请成功,否则申请失败。
(2)释放gpio引脚
void gpio_free(unsigned gpio);
3.2. gpio引脚方向设置
声明在include/asm-generic/gpio.h
头文件中,由 CONFIG_GPIOLIB
宏定义控制是否使能。
(1)设置某个gpio引脚方向为输入
static inline int gpio_direction_input(unsigned gpio);
(2)设置某个gpio引脚方向为输出
static inline int gpio_direction_output(unsigned gpio, int value)
value参数表示引脚输出默认值。
3.3. gpio设置引脚值与获取引脚值
在arch/arm/include/asm/gpio.h
文件中声明。
/* The trivial gpiolib dispatchers */
#define gpio_get_value __gpio_get_value
#define gpio_set_value __gpio_set_value
在include/asm-generic/gpio.h
文件中实现,由 CONFIG_GPIOLIB
宏定义控制是否使能。
/* A platform's <asm/gpio.h> code may want to inline the I/O calls when
* the GPIO is constant and refers to some always-present controller,
* giving direct access to chip registers and tight bitbanging loops.
*/
static inline int __gpio_get_value(unsigned gpio)
return gpiod_get_raw_value(gpio_to_desc(gpio));
static inline void __gpio_set_value(unsigned gpio, int value)
return gpiod_set_raw_value(gpio_to_desc(gpio), value);
四、GPIO子系统大概实现框架
1. 如何找到imx6ull对应的gpio子系统驱动
驱动是根据设备树节点的兼容性(compatible)来匹配驱动。
imx6ull.dtsi描述文件中,gpio的兼容性如图:
在内核源码中全局搜索该兼容性字符串,寻找对应驱动,找到驱动文件为:drivers/gpio/gpio-mxc.c。
2. 驱动模块的加载
该驱动模块的加载函数如下:
3. 平台驱动结构体
在驱动模块入口中,调用platform_driver_register
API来注册一个平台设备驱动,注册的驱动结构体如下:
4. 驱动probe挂载函数
驱动probe挂载函数实现中,会有一系列 mxc_xxx 函数,这些函数就是和imx6ull硬件平台相关的。
(1)其中 mxc_gpio_port 结构体就是对 imx6ull gpio 外设的抽象,结构体定义如下。
(2)mxc_gpio_get_hw 函数用来获取 gpio外设的硬件相关数据。
imx35_gpio_hwdata结构体定义如下:
(3)获取gpio1外设的寄存器地址和虚拟地址映射。
(4)关闭GPIO1所有IO中断,设置中断服务函数为 mx3_gpio_irq_handler。
(5)bgpio_chip结构体初始化
bgpio_chip结构体的定义如下:
其中gc成员是gpio_chip结构体类型,在include/linux/gpio/driver.h
文件中定义,由CONFIG_GPIOLIB宏定义控制。
bgpio_init函数主要完成bgc->gc
中有关GPIO的操作函数初始化,并将GPIO外设的寄存器地址赋值给bgc的成员变量reg_xxx中,所以后续只需要bgc结构体,就可以完成对GPIO外设的操作。
(6)向内核注册gpio_chip,也就是bgc.gc
,注册完成后,就可以在驱动中使用GPIO子系统提供的各种API函数。
以上是关于i.MX6ULL驱动开发 | 07 -gpio子系统的主要内容,如果未能解决你的问题,请参考以下文章
i.MX6ULL驱动开发 | 36 - 注册spilcd为framebuffer设备并使用lvgl测试
i.MX6ULL驱动开发 | 36 - 注册spilcd为framebuffer设备并使用lvgl测试
i.MX6ULL驱动开发 | 15 - Linux UART 驱动框架