GPIO 实例

本节适配硬件才能观察 GPIO 的情况, 因此需要开发板.

1 基本模板

首先改变原本的设备树.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 // LED
leds {
compatible = "gpio-leds";
// status = "okay";
// we will test our driver
status = "disabled";
heart_led {
label = "run";
gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>;
linux,default-trigger = "heartbeat";
default-state = "off";
};
};

my_managed_device {
compatible = "my,pin-manager";
my-run-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>;
status = "okay";
};

内核程序.

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
119
120
121
122
123
124
125
126
127
128
129
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h> // for kzalloc, kfree
#include <linux/device.h> // for device attributes
#include <linux/of.h>

/*
* my_managed_device {
* compatible = "my,pin-manager";
* my-run-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>;
* status = "okay";
* };
*/
struct led_data {
struct gpio_desc *led_desc;
};

/* sysfs store function */
static ssize_t led_ctrl_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_data *priv = dev_get_drvdata(dev);
int ret, state;

ret = kstrtoint(buf, 10, &state);
if (ret < 0) return ret;

gpiod_set_value(priv->led_desc, state);
dev_info(dev, "User set LED to %d\n", state);

return count;
}

/* sysfs show function */
static ssize_t led_ctrl_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct led_data *priv = dev_get_drvdata(dev);
int val = gpiod_get_value(priv->led_desc);
return sprintf(buf, "%d\n", val);
}

static DEVICE_ATTR(ctrl, 0644, led_ctrl_show, led_ctrl_store);

static int led_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct led_data *priv;
int ret;

dev_info(dev, "LED Driver: Probing (Manual Mode)...\n");

/*
* [devm version]: priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
*/
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;

/*
* [devm version]: priv->led_desc = devm_gpiod_get(dev, "my_led", GPIOD_OUT_LOW);
*/
priv->led_desc = gpiod_get(dev, "my-run", GPIOD_OUT_LOW);
if (IS_ERR(priv->led_desc)) {
dev_err(dev, "Failed to get GPIO resource\n");
ret = PTR_ERR(priv->led_desc);
goto err_free_mem;
}

platform_set_drvdata(pdev, priv);

// create sysfs node
ret = device_create_file(dev, &dev_attr_ctrl);
if (ret) {
dev_err(dev, "Failed to create sysfs node\n");
goto err_free_gpio;
}

dev_info(dev, "Probe successful.\n");
return 0;

err_free_gpio:
gpiod_put(priv->led_desc);
err_free_mem:
kfree(priv);
return ret;
}

static int led_remove(struct platform_device *pdev)
{
struct led_data *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;

device_remove_file(dev, &dev_attr_ctrl);

gpiod_set_value(priv->led_desc, 0);


// [devm version]: don't need to put GPIO
gpiod_put(priv->led_desc);
dev_info(dev, "GPIO released.\n");

// [devm version]: don't need to free memory
kfree(priv);
dev_info(dev, "Memory freed.\n");
return 0;
}

static const struct of_device_id led_match[] = {
{ .compatible = "my,pin-manager" },
{ },
};

MODULE_DEVICE_TABLE(of, led_match);

static struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "demo-led-driver",
.of_match_table = led_match,
},
};

module_platform_driver(led_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jvle");

芯片的 GPIO 控制器开发厂商已经帮我们完成了.

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
pinctrl: pinctrl {
compatible = "rockchip,rk3506-pinctrl";
rockchip,grf = <&ioc_grf>;
rockchip,ioc1 = <&ioc1>;
rockchip,pmu = <&ioc_pmu>;
rockchip,rmio = <&grf_pmu>;
#address-cells = <1>;
#size-cells = <1>;
ranges;

gpio0: gpio@ff940000 {
compatible = "rockchip,gpio-bank";
reg = <0xff940000 0x200>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru PCLK_GPIO0>, <&cru DBCLK_GPIO0>;

gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 0 32>;
interrupt-controller;
#interrupt-cells = <2>;
};

gpio1: gpio@ff870000 {
compatible = "rockchip,gpio-bank";
reg = <0xff870000 0x200>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;

clocks = <&cru PCLK_GPIO1>, <&cru DBCLK_GPIO1>;

gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 32 32>;
interrupt-controller;
#interrupt-cells = <2>;
};

gpio2: gpio@ff1c0000 {
compatible = "rockchip,gpio-bank";
reg = <0xff1c0000 0x200>;
interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru PCLK_GPIO2>, <&cru DBCLK_GPIO2>;

gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 64 32>;
interrupt-controller;
#interrupt-cells = <2>;
};

gpio3: gpio@ff1d0000 {
compatible = "rockchip,gpio-bank";
reg = <0xff1d0000 0x200>;
interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;

gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 96 32>;
interrupt-controller;
#interrupt-cells = <2>;
};

gpio4: gpio@ff1e0000 {
compatible = "rockchip,gpio-bank";
reg = <0xff1e0000 0x200>;
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru PCLK_GPIO4>, <&cru DBCLK_GPIO4>;

gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 128 32>;
interrupt-controller;
#interrupt-cells = <2>;
};
};

以 RK3506G 为例. 这里有 4 组 GPIO.

对 Rockchip 的 GPIO 编号计算公式为:

1
GPIO_ID = (Bank * 32) + (Group * 8) + Index

对于开发板上的 LED, 查看手册得知, 编号为 GPIO1_B3, 即 gpiochip1 的 11 号.

可以看看 GPIO 现在的情况.

加载.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ insmod /tmp/gpio.ko
$ gpioinfo
...
gpiochip1 - 32 lines:
line 0: unnamed unused input active-high
line 1: unnamed unused input active-high
line 2: unnamed unused input active-high
line 3: unnamed unused input active-high
line 4: unnamed unused input active-high
line 5: unnamed unused input active-high
line 6: unnamed unused input active-high
line 7: unnamed unused input active-high
line 8: unnamed unused input active-high
line 9: unnamed unused input active-high
line 10: unnamed unused input active-high
line 11: unnamed "my-run" output active-low [used]
...

卸载.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ rmmod /tmp/gpio.ko
$ gpioinfo
...
gpiochip1 - 32 lines:
line 0: unnamed unused input active-high
line 1: unnamed unused input active-high
line 2: unnamed unused input active-high
line 3: unnamed unused input active-high
line 4: unnamed unused input active-high
line 5: unnamed unused input active-high
line 6: unnamed unused input active-high
line 7: unnamed unused input active-high
line 8: unnamed unused input active-high
line 9: unnamed unused input active-high
line 10: unnamed unused input active-high
line 11: unnamed unused output active-high

查看内核打印信息.

1
2
3
4
5
6
7
# dmesg
[ 1057.636134] demo-led-driver my_managed_device: LED Driver: Probing (Manual Mode)...
[ 1057.636284] demo-led-driver my_managed_device: Probe successful.
[ 1070.288399] demo-led-driver my_managed_device: User set LED to 1
[ 1084.954286] demo-led-driver my_managed_device: User set LED to 0
[ 1091.455914] demo-led-driver my_managed_device: GPIO released.
[ 1091.455960] demo-led-driver my_managed_device: Memory freed.

指令执行情况.

1
2
3
4
$ echo 1 >> /sys/devices/platform/my_managed_device/ctrl
# 灯亮
$ echo 0 >> /sys/devices/platform/my_managed_device/ctrl
# 灯灭