07、gpio接口与设备树
设备树写法:
[<name>-gpio]
of_get_named_gpio(<name>-gpio)
(整数型)gpiod_get_optional(<name>-gpio)
(描述符)
[<name>-gpios]
gpiod_get_optional(name,索引值)
(描述符)of_get_named_gpio(name,索引值)
(整数型)gpios
of_get_gpio API获取(整数型)
驱动解析方法:
一、GPIO OF相关接口汇总
Linux 内核提供了几个与 GPIO 有关的 OF 函数,常用的几个 OF 函数如下所示:
1、 of_gpio_named_count 函数
of_gpio_named_count 函数用于获取设备树某个属性里面定义了几个 GPIO 信息。比如设备树里有个属性叫"my_gpios",里面写了3个GPIO信息,这个函数就会返回3。
int of_gpio_named_count(struct device_node *np, const char *propname)
/*
参数:
* `np`:设备节点
* `propname`:要统计的属性名(如"my_gpios")
*/
2
3
4
5
6
7
2、 of_gpio_count 函数
功能和上面一样,但只统计固定属性"gpios"里的GPIO数量。 比如设备树的"gpios"属性写了5个GPIO,这个函数返回5。
int of_gpio_count(struct device_node *np)
/*
参数:
* `np`:设备节点
*/
2
3
4
5
6
3、 of_get_named_gpio 函数
这个函数把设备树里的GPIO描述(如 <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>
) 转换成Linux内核能直接使用的GPIO编号。
`int of_get_named_gpio(struct device_node *np, const char *propname, int index)`
/*
参数:
* `np`:设备节点
* `propname`:GPIO所在的属性名(如"gpios"或"my\_gpios")
* `index`:要获取的第几个GPIO(从0开始)
*/
2
3
4
5
6
7
总结区别
- 前两个函数只统计数量,第三个函数获取实际GPIO编号
- 第一个函数可统计任意属性,第二个仅限"gpios"属性
二、传统的基于整数的接口和设备树
该接口依赖于如下头文件:
#include <linux/of_gpio.h>
2.1、新的说明符号
使用传统的基于整数的接口时,如果要在驱动程序内支持DT,则应该记住两个函数:of_get_named_gpio()和of_get_named_gpio_count():
int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
int of_get_named_gpio_count(struct device_node *np,
const char* propname)
2
3
4
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h> /* 平台设备*/
#include <linux/interrupt.h> /* IRQ */
#include <linux/gpio.h> /* 遗留的基于整数的GPIO */
#include <linux/of_gpio.h> /* of_gpio* 函数 */
#include <linux/of.h> /* DT*/
/*
* 例如以下节点
*
* foo_device {
* compatible = "packt, gpio-legacy-sample";
* leds-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>, // 红色
* <&gpio2 16 GPIO_ACTIVE_HIGH>, // 蓝色
*
* btn1-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>,
* btn2-gpios = <&gpio2 31 GPIO_ACTIVE_LOW>,
* };
*/
static unsigned int gpio_red, gpio_green, gpio_btn1, gpio_btn2;
static unsigned int irq;
static irq_handler_t btn1_pushed_irq_handler(unsigned int irq, void
*dev_id,
struct pt_regs *regs)
{
/* 该函数的内容保持不变*/
[...]
}
static const struct of_device_id gpio_dt_ids[] = {
{ .compatible = "packt,gpio-legacy-sample", },
{ /* 哨兵 */ }
};
static int my_pdrv_probe (struct platform_device *pdev)
{
int retval;
struct device_node *np = &pdev->dev.of_node;
if (!np)
return ERR_PTR(-ENOENT);
gpio_red = of_get_named_gpio(np, "leds", 0);
gpio_green = of_get_named_gpio(np, "leds", 1);
gpio_btn1 = of_get_named_gpio(np, "btn1", 0);
gpio_btn2 = of_get_named_gpio(np, "btn2", 0);
gpio_request(gpio_green, "green-led");
gpio_request(gpio_red, "red-led");
gpio_request(gpio_btn1, "button-1");
gpio_request(gpio_btn2, "button-2");
/* 配置GPIO和请求IRQ的代码保持不变 */
[...]
return 0;
}
static void my_pdrv_remove(struct platform_device *pdev)
{
/* 该函数的内容保持不变 */
[...]
}
static struct platform_driver mypdrv = {
.probe = my_pdrv_probe,
.remove = my_pdrv_remove,
.driver = {
.name = "gpio_legacy_sample",
.of_match_table = of_match_ptr(gpio_dt_ids),
.owner = THIS_MODULE,
},
};
module_platform_driver(mypdrv);
MODULE_LICENSE("GPL");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2.2、旧的说明符号
有些驱动程序仍然支持旧的说明符,其中GPIO属性命名为[<name>-gpio]
或gpios
。在这种情况下,应该通过of_get_gpio()和of_gpio_count()来使用未命名的API版本:
int of_gpio_count(struct device_node *np)
int of_get_gpio(struct device_node *np, int index)
2
DT节点像下面这样:
my_node@addr {
compatible = "[...]";
gpios = <&gpio1 2 0>, /* INT */
<&gpio1 5 0>; /* RST */
[...]
};
2
3
4
5
6
7
驱动程序中的代码如下所示:
struct device_node *np = dev->of_node;
if (!np)
return ERR_PTR(-ENOENT);
int n_gpios = of_gpio_count(); /* 将返回2 */
int gpio_int = of_get_gpio(np, 0);
if (!gpio_is_valid(gpio_int)) {
dev_err(dev, "failed to get interrupt gpio\n");
return ERR_PTR(-EINVAL);
}
gpio_rst = of_get_gpio(np, 1);
if (!gpio_is_valid(pdata->gpio_rst)) {
dev_err(dev, "failed to get reset gpio\n");
return ERR_PTR(-EINVAL);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
三、设备树内将GPIO映射到IRQ
在设备树中,可以通过两个属性将GPIO信号映射为中断信号:
- interrupt-parent:指定当前GPIO所属的控制器设备节点
- interrupts:设置中断参数(通常包含两个值)
具体来说:
- 第一个参数通常是GPIO编号,表示要转换为中断的物理引脚
- 第二个参数表示触发条件(比如上升沿/下降沿或高低电平)
不同GPIO控制器可能需要不同数量的参数(由控制器节点的#interrupt-cells
属性决定),但大多数情况下使用这两个值即可完成映射。这种机制同时适用于传统接口和现代的描述符接口。
在任何情况下,中断说明符总是依赖于其父节点(设置中断控制器的节点),请参考内核源文件中的绑定文档:
gpio4: gpio4 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
my_label: node@0 {
reg = <0>;
spi-max-frequency = <1000000>;
interrupt-parent = <&gpio4>;
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
};
2
3
4
5
6
7
8
9
10
11
12
13