GPIO
GPIO 简介
GPIO(General Purpose Input/Output,通用输入输出)。可能是芯片自带的,也可能通过 I2C、SPI 接口扩展。GPIO 子系统的作用是既能支持芯片本身的 GPIO,也能支持扩展的 GPIO,为系统提供统一的、简便的访问接口,从而实现输入、输出、中断等功能。芯片内部有很多引脚,这些引脚可以接到 GPIO 模块,也可以接到 I2C 等模块。通过 Pinctrl 子系统来选择引脚的功能 (mux function)、配置引脚。当一个引脚被复用为 GPIO 功能时,可设置其方向、读取或写入电平值。
GPIO 开发框架
Linux 内核为 GPIO 提供了开发基本框架:
核心 API
GPIO Chip 加载完成后,即可通过 Linux 内核提供的标准接口控制 GPIO。GPIOLIB 向上提供了常用的基本接口如下,这些接口定义在 linux/gpio/consumer.h 中:
| API | Function | 备注 |
|---|---|---|
gpiod_get / devm_gpiod_get | 获取 idx 为 0 的句柄,返回 gpio_desc 类型的指针,适用于单个引脚 | 示例:gpiod_get(dev, "client", 0);DTS 写法:client-gpios = <&gpio_peri MT_GPIO_GLB_NO(81) GPIO_ACTIVE_HIGH>; |
gpiod_get_index / devm_gpiod_get_index | 获取指定 idx 的句柄,返回 gpio_desc 类型的指针,适用于定义多个 GPIO 引脚 | 示例:gpiod_get_index(dev, "client", 1);DTS 写法:client-gpios = <&gpio_peri MT_GPIO_GLB_NO(81) GPIO_ACTIVE_HIGH>, <&gpio_peri MT_GPIO_GLB_NO(237) GPIO_ACTIVE_HIGH>; |
gpiod_get_array / devm_gpiod_get_array | 一次性获取多个 GPIO 引脚,返回 gpio_descs 类型数组指针,通过 descs->ndescs 获取数量 | 示例:descs = gpiod_get_array(dev, "client", 0);DTS 写法:client-gpios = <&gpio_peri MT_GPIO_GLB_NO(81) GPIO_ACTIVE_HIGH>, <&gpio_peri MT_GPIO_GLB_NO(236) GPIO_ACTIVE_HIGH>, <&gpio_peri MT_GPIO_GLB_NO(237) GPIO_ACTIVE_HIGH>; |
gpiod_get_value | 获取 GPIO 的电平值 | desc 需通过 gpiod_get_xxx 获取 |
gpiod_set_value | 设置 GPIO 的电平值 | desc 需通过 gpiod_get_xxx 获取 |
gpiod_direction_input | 设置 GPIO 为输入功能 | 示例:gpiod_direction_input(desc); desc 需通过 gpiod_get_xxx 获取 |
gpiod_direction_output | 设置 GPIO 为输出功能 | 示例:gpiod_direction_output(desc, value); desc 需通过 gpiod_get_xxx 获取 |
gpiod_put_array | 释放多个 GPIO 资源,与 gpiod_get_array 配对使用 | desc 需通过 gpiod_get_xxx 获取 |
gpiod_free | 释放单个 GPIO 资源,与 gpiod_get / gpiod_get_index 配对使用 | 示例:gpiod_free(desc); desc 需通过 gpiod_get_xxx 获取 |
约束说明
SoC 内部有两个 GPIO 控制器,0–20 称为 gpio_sm,21–237 称为 gpio_peri。因此在配置引脚的时候:
- GPIO 0–20 要指定
gpio_sm作为 GPIO 控制器; - GPIO 21–237 要指定
gpio_peri作为 GPIO 控制器。
以下以 GPIO 5、20、21、234 举例,仅示范如何编写 DTS,实际使用时请按照硬件要求配置。
// 普通引脚使用片段
client-gpios = <&gpio_sm MT_GPIO_GLB_NO(5) GPIO_ACTIVE_HIGH>, // gpio 5 range [0~20] 使用 gpio_sm 作为 gpio 控制器
<&gpio_sm MT_GPIO_GLB_NO(20) GPIO_ACTIVE_HIGH>, // gpio 20 range [0~20] 使用 gpio_sm 作为 gpio 控制器
<&gpio_peri MT_GPIO_GLB_NO(21) GPIO_ACTIVE_HIGH>, // gpio 21 range [21~237] 使用 gpio_peri 作为 gpio 控制器
<&gpio_peri MT_GPIO_GLB_NO(234) GPIO_ACTIVE_HIGH>; // gpio 234 range [21~237] 使用 gpio_peri 作为 gpio 控制器
// 中断
interrupts-extended = <&gpio_peri MT_GPIO_GLB_NO(201) IRQ_TYPE_EDGE_RISING>, // gpio 201 range [21~237] 使用 gpio_peri 作为 gpio 控制器
<&gpio_sm MT_GPIO_GLB_NO(20) IRQ_TYPE_EDGE_RISING>; // gpio 20 range [0~20] 使用 gpio_sm 作为 gpio 控制器
注:GPIO_ACTIVE_HIGH 为输出高电平使能对应外设,IRQ_TYPE_EDGE_RISING 为上升沿触发中断。
配置流程
步骤 1:编写 DTS
gpio_peri_client: gpio_peri_client {
compatible = "mthreads,m1000-gpiocli";
interrupts-extended =
<&gpio_peri MT_GPIO_GLB_NO(21) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(22) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(23) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(24) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(201) IRQ_TYPE_EDGE_RISING>;
client-gpios =
<&gpio_peri MT_GPIO_GLB_NO(236) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(237) GPIO_ACTIVE_HIGH>;
status = "okay"; // okay, disabled
};
步骤 2:编写对应驱动
/* compatible */
static const struct of_device_id gpio_client_id[] = {
{ .compatible = "mthreads,m1000-gpiocli" },
{},
};
/* platform_driver */
static struct platform_driver gpio_client_driver = {
.probe = gpio_client_probe,
.remove = gpio_client_remove,
.driver = {
.name = GPIO_CLIENT_DRIVER_NAME,
.of_match_table = gpio_client_id,
},
};
/* probe */
static int gpio_client_probe(struct platform_device *pdev) {
...
/* 获取 gpio desc */
gpio_client_gpios = gpiod_get_array(&pdev->dev, "client", 0); // 对应 DTS 中的 client-gpios = <...>
/* 获取 gpio irq,参数 0 表示获取 gpio 21 的中断号,参考上述 DTS 配置 */
platform_get_irq(pdev, 0);
devm_request_irq(...);
...
}
设置输入、输出或其他功能:
gpiod_direction_input(desc);
gpiod_direction_output(desc);
gpiod_set_value(...);
gpiod_set_config(...);
开发参考
当前内核已经完成了 GPIO controller 的开发,用户只需编写 GPIO client 相关的驱动即可。
GPIO 通常不作为单独的驱动存在,而是嵌入在其他驱动中,例如基于 I2S、I2C 的设备驱动中,配合这些驱动完成一些中断、输入、输出、上拉、下拉等功能。
用户可参考以下文件中的代码片段,在自己的驱动中调用相关接口,完成业务逻辑:
drivers/pinctrl/mthreads/uio_pdrv_gpio.cdrivers/gpio/gpio-mthreads-client.c
DTS 的编写可参考 m1000-main.dtsi 中的写法,搜索 gpio_peri_client 即可找到:
gpio_peri_client: gpio_peri_client {
compatible = "mthreads,m1000-gpiocli";
interrupts-extended =
<&gpio_peri MT_GPIO_GLB_NO(21) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(22) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(23) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(24) IRQ_TYPE_LEVEL_LOW>,
<&gpio_peri MT_GPIO_GLB_NO(201) IRQ_TYPE_EDGE_RISING>;
client-gpios =
<&gpio_peri MT_GPIO_GLB_NO(81) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(92) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(93) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(94) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(228) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(234) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(235) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(236) GPIO_ACTIVE_HIGH>,
<&gpio_peri MT_GPIO_GLB_NO(237) GPIO_ACTIVE_HIGH>;
status = "okay"; // okay, disabled
};
调试方法说明
调试具体引脚
find /sys/ -name "gpio-ctrl"
输出类似以下结果:
/sys/devices/platform/SPIO0001:00/gpio-ctrl # gpio_sm 调试信息,对应 gpio 0-20
/sys/devices/platform/APIO0001:00/gpio-ctrl # gpio_peri 调试信息,对应 gpio 21-237
以 gpio_peri 为例,查看第 21、22 号引脚:
cd /sys/devices/platform/APIO0001:00/gpio-ctrl/peri_padc
# 将需要 debug 的引脚写入 index
echo 21 > index
echo 22 > index
# 查看引脚状态
cat stat
gicv3 interrupt cnt: 1
pin ctrl fs output_val ren oen input_val inte int_pos int_neg srcpnd intmask intmask_l2 inten_l2 intstat_l2 irq_cnt
21 100 0 0 1 in 0 0 1 1 0 0 0 0 0 0
22 100 0 0 1 in 0 0 1 1 0 0 0 0 0 0
# 通过命令清除
echo -1 > clear