0%

RK3568 GPIO的使用

一.gpio查看命令

//查看引脚状态

1
cat /sys/kernel/debug/gpio 

//查看gpio引脚的复用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat /sys/kernel/debug/pinctrl/pinctrl/pinmux-pins

Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (gpio0-0): wireless-wlan (GPIO UNCLAIMED) function wireless-wlan group wifi-wake-host
pin 1 (gpio0-1): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 2 (gpio0-2): (MUX UNCLAIMED) gpio0:2
pin 3 (gpio0-3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 4 (gpio0-4): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 5 (gpio0-5): (MUX UNCLAIMED) gpio0:5
pin 6 (gpio0-6): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 7 (gpio0-7): (MUX UNCLAIMED) gpio0:7
pin 8 (gpio0-8): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 9 (gpio0-9): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 10 (gpio0-10): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 11 (gpio0-11): ff050000.i2c (GPIO UNCLAIMED) function i2c1 group i2c1-xfer
pin 12 (gpio0-12): ff050000.i2c (GPIO UNCLAIMED) function i2c1 group i2c1-xfer
pin 13 (gpio0-13): (MUX UNCLAIMED) (GPIO UNCLAIMED)

二.从用户层控制gpio

要从用户层控制gpio前提是这个引脚是gpio或者已经复用为gpio

gpio bank:

RK3568 有 5 组 GPIO bank:GPIO0~GPIO4,每组又以 A0-A7、B0-B7、 C0-C7、 D0-D7 作为编号区分,常用以下公式计算引脚:

GPIO 引脚计算公式:pin = bank 32 + number
GPIO 小组编号计算公式:number = group
8 + X

下面演示 GPIO3_A7 引脚计算方法:

bank = 3; // GPIO3_A7 => 3, bank ∈ [0,4]
group = 0; // GPIO3_A7 => 0, group ∈ {(A=0), (B=1), (C=2), (D=3)}
X = 7; // GPIO3_A7 => 7, X ∈ [0,7]

GPIO3_A7 对应的设备树属性描述为:<&gpio3 7 IRQ_TYPE_EDGE_RISING>由kernel/include/dt-bindings/pinctrl/rockchip.h的宏定义可知。

gpiochipX:

5b4f6d54320ae41c37d0d071a31b51d8.png

当前SOC 一共包含5 个 GPIO 控制器,分别是:GPIO1,GPIO2,GPIO3,GPIO4,GPIO5。在这里,分别是gpiochip0 、gpiochip32 、gpiochip64 、gpiochip96 、gpiochip128 这5个文件夹,每个gpiochipX 文件夹用来管理一组GPIO。

export:

export用于将指定编号的GPIO 引脚导出。在使用GPIO 引脚之前,需要将其导出,导出成功之后才能使用它。注意export 文件是只写文件,不能读取,将一个指定的编号写入到export 文件中即可将对应的GPIO 引脚导出,例如:

echo 0 > export #导出编号为0的GPIO 引脚。对应当前SOC,也就是GPIO1_IO0。

导出成功之后会发现在/sys/class/gpio 目录下生成了一个名为gpio0 的文件夹(gpioX, X 表示对应的编号)。这个文件夹就是导出来的GPIO 引脚对应的文件夹,用于管理、控制该GPIO 引脚。

unexport:

unexport将导出的GPIO 引脚删除。当使用完GPIO 引脚之后,我们需要将导出的引脚删除,同样该文件也是只写、不可读的。例如:

1
echo 0 > unexport # 删除导出的编号为0的GPIO 引脚1

删除成功之后,之前生产的gpio0 文件夹就会消失。

导出引脚echo export:

f564b5d049017bb2329d3ad39b1f60d9.png

向/sys/class/gpio/export写入此编号,比如10号引脚,在shell中可以通过以下命令实现,

1
echo 10 > /sys/class/gpio/export

命令成功后生成/sys/class/gpio/gpio10目录,

如果没有出现相应的目录,说明此引脚不可导出。

设置gpio的direction:

direction文件,定义输入输入方向,可以通过下面命令定义为输出

1
echo out > /sys/class/gpio/gpio10/direction

direction接受的参数:in, out,

三.内核空间控制gpio

Linux pinctrl 子系统:

复用gpio2 PB1为gpio功能:

1
2
3
4
5
6
cam {
​ /omit-if-no-ref/
​ cam_pow_en: cam-pow-en {
​ rockchip,pins = <2 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
​ };
​};

上下拉配置:

//上拉
pcfg-pull-up
//下拉
pcfg-pull-down

linux gpio子系统:

gpio_request函数:

int gpio_request(unsigned gpio, const char *label);

gpio:要申请的gpio标号(可以通过gpio bank计算得来)。

label:要申请的gpio取的名字。

可通过cat /sys/kernel/debug/gpio 命令查看gpio是否申请成功。

gpio_free函数:

void gpio_free(unsigned gpio)

gpio:要释放的gpio标号。

gpio_direction_input函数:此函数用于设置某个GPIO为输入

int gpio_direction_input(unsigned gpio)

gpio:要设置为输入的GPIO标号

gpio_direction_output函数:此函数用于设置某个GPIO为输出

int gpio_direction_output(unsigned gpio, int value)

gpio:要设置为输出的GPIO标号

value:GPIO默认输出值

gpio_get_value函数:此函数用于获取某个GPIO的值(0或1)

int __gpio_get_value(unsigned gpio)

gpio:要获取的GPIO标号

gpio_set_value函数:此函数用于设置某个GPIO的值

void __gpio_set_value(unsigned gpio, int value)

gpio:要设置的GPIO标号

value:要设置的值

驱动示例
下面就是一个简单的驱动,可放在rk3568/drivers/gpio/gpio-e.c

编译时,别忘了加上makefile

1
2
3
# rk3568/kernel/drivers/gpio/Makefile

obj-y = gpio-e.o

c代码

1
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <dt-bindings/gpio/gpio.h>


#define DEBUG
#ifdef DEBUG

#define LOG(fmt, args...) pr_info("[E GPIO]: " fmt, ##args)
#else
#define LOG(fmt, args...)
#endif

struct e_gpio{
int io;
int enable;
int period_ms;
};

static int e_gpio_parse_dt(struct device* dev, struct e_gpio* data)
{
struct device_node* node = dev->of_node;
int gpio;
enum of_gpio_flags flag;
int ret;

gpio = of_get_named_gpio_flags(node, "gpio", 0, &flag);
if(gpio_is_valid(gpio)){
data->io = gpio;
data->enable = (flag == GPIO_ACTIVE_HIGH) ? 1: 0;
}else{
LOG("cannot parse gpio!");
data->io = -1;
return -ENODEV;
}

ret = of_property_read_u32(node, "period-ms", &data->period_ms);
if(ret < 0)
data->period_ms = -1;

return 0;
}

static void e_setup_gpio(struct device* dev, struct e_gpio* data)
{
int ret;

if(gpio_is_valid(data->io)){
ret = devm_gpio_request(dev, data->io, "e-gpio");
//ret = devm_gpio_request_one(dev, data->io, GPIOF_DIR_OUT, NULL);
if(ret < 0){
LOG("failed to get gpio!");
return;
}

gpio_direction_output(data->io, data->enable);
if(data->period_ms > 0){
msleep(data->period_ms);
devm_gpio_free(dev, data->io);
}
}
LOG("GPIO setup done!");
}

static int e_gpio_probe(struct platform_device* pdev)
{
struct device* dev = &pdev->dev;
int ret;
struct e_gpio* data;

data = devm_kzalloc(dev, sizeof(struct e_gpio), GFP_KERNEL);
if(!data)
return -ENOMEM;

ret = e_gpio_parse_dt(dev, data);
if(ret < 0)
return -ENODEV;

e_setup_gpio(dev, data);
return 0;
}

static struct of_device_id e_gpio_of_match[] = {
{.compatible = "e,gpio"},
{}
};
MODULE_DEVICE_TABLE(of, e_gpio_of_match);

static struct platform_driver e_gpio_driver = {
.probe = e_gpio_probe,
.driver = {
.name = "e-gpio",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(e_gpio_of_match),
},
};

static int __init e_gpio_init(void)
{
LOG("e gpio init!");
return platform_driver_register(&e_gpio_driver);
}
module_init(e_gpio_init)

static void __exit e_gpio_exit(void)
{
LOG("e gpio exit");
platform_driver_unregister(&e_gpio_driver);
}
module_exit(e_gpio_exit)

MODULE_AUTHOR("XXX");
MODULE_DESCRIPTION("e gpio driver");
MODULE_LICENSE("GPL");

3 对应的DTS

1
2
3
4
5
6
gpio:e-gpio{
compatible="e,gpio";
gpio = <&gpio2 RK_PD7 GPIO_ACTIVE_HIGH>;
// period-ms = <15>;
status = "okay";
};

整体功能比较简单,唯一需要解释的是period-ms,这代表开机时先把gpio拉高/低一段时间(如例子中为15ms),然后释放掉对应的gpio资源。