GPIO 描述符消费者接口

本文档描述了 GPIO 框架的消费者接口。

GPIO 消费者指南

如果驱动程序在没有标准 GPIO 调用的情况下无法工作,则应具有依赖于 GPIOLIB 或选择 GPIOLIB 的 Kconfig 条目。通过包含以下文件,可以使用允许驱动程序获取和使用 GPIO 的函数

#include <linux/gpio/consumer.h>

如果禁用了 GPIOLIB,则头文件中的所有函数都有静态内联存根。当调用这些存根时,它们将发出警告。这些存根用于两种用例

  • 使用例如 COMPILE_TEST 的简单编译覆盖 - 当前平台是否启用或选择 GPIOLIB 并不重要,因为我们无论如何都不会执行系统。

  • 真正的可选 GPIOLIB 支持 - 驱动程序在某些系统的某些编译时配置中实际上不使用 GPIO,但在其他编译时配置下将使用它。在这种情况下,消费者必须确保不调用这些函数,否则用户将遇到可能被认为具有恐吓性的控制台警告。将真正的可选 GPIOLIB 使用与调用 [devm_]gpiod_get_optional() 结合使用是一个坏主意,并且会导致奇怪的错误消息。将可选 GPIOLIB 与普通的 getter 函数一起使用:当您这样做时,应该预期一些错误处理的开放编码。

所有使用基于描述符的 GPIO 接口的函数都带有前缀 gpiod_。前缀 gpio_ 用于旧版接口。内核中没有其他函数应使用这些前缀。强烈建议不要使用旧版函数,新代码应仅使用 <linux/gpio/consumer.h> 和描述符。

获取和释放 GPIO

使用基于描述符的接口,GPIO 通过调用 gpiod_get() 函数之一获取不透明且不可伪造的处理程序来标识。像许多其他内核子系统一样,gpiod_get() 接受将使用 GPIO 的设备,以及请求的 GPIO 应履行的功能

struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
                            enum gpiod_flags flags)

如果某个函数是通过一起使用多个 GPIO 来实现的(例如,一个显示数字的简单 LED 设备),则可以指定一个附加的索引参数

struct gpio_desc *gpiod_get_index(struct device *dev,
                                  const char *con_id, unsigned int idx,
                                  enum gpiod_flags flags)

有关 DeviceTree 中 con_id 参数的更详细描述,请参阅 GPIO 映射

flags 参数用于可选地指定 GPIO 的方向和初始值。值可以是

  • GPIOD_ASIS 或 0,以完全不初始化 GPIO。方向必须稍后通过专用函数之一设置。

  • GPIOD_IN,将 GPIO 初始化为输入。

  • GPIOD_OUT_LOW,将 GPIO 初始化为输出,值为 0。

  • GPIOD_OUT_HIGH,将 GPIO 初始化为输出,值为 1。

  • GPIOD_OUT_LOW_OPEN_DRAIN 与 GPIOD_OUT_LOW 相同,但还强制该线路在电气上与开漏一起使用。

  • GPIOD_OUT_HIGH_OPEN_DRAIN 与 GPIOD_OUT_HIGH 相同,但还强制该线路在电气上与开漏一起使用。

请注意,初始值是逻辑的,物理线路电平取决于线路是配置为高电平有效还是低电平有效(请参阅 低电平有效和开漏语义)。

最后两个标志用于强制使用开漏的用例,例如 I2C:如果线路在映射中尚未配置为开漏(请参阅 GPIO 映射),则无论如何都会强制使用开漏,并且将打印警告,指示需要更新板配置以匹配用例。

这两个函数都返回有效的 GPIO 描述符,或者返回可通过 IS_ERR() 检查的错误代码(它们永远不会返回 NULL 指针)。当且仅当没有 GPIO 分配给设备/功能/索引三元组时,才会返回 -ENOENT,其他错误代码用于分配了 GPIO 但在尝试获取它时发生错误的情况。这有助于区分仅有的错误和可选 GPIO 参数的 GPIO 缺失。对于 GPIO 是可选的常见模式,可以使用 gpiod_get_optional()gpiod_get_index_optional() 函数。如果没有将 GPIO 分配给请求的功能,则这些函数会返回 NULL 而不是 -ENOENT

struct gpio_desc *gpiod_get_optional(struct device *dev,
                                     const char *con_id,
                                     enum gpiod_flags flags)

struct gpio_desc *gpiod_get_index_optional(struct device *dev,
                                           const char *con_id,
                                           unsigned int index,
                                           enum gpiod_flags flags)

请注意,gpio_get*_optional() 函数(及其托管变体)与 gpiolib API 的其余部分不同,当 gpiolib 支持禁用时,也会返回 NULL。这对驱动程序作者很有帮助,因为他们不需要特殊处理 -ENOSYS 返回代码。但是,系统集成商应注意在需要它的系统上启用 gpiolib。

对于使用多个 GPIO 的函数,所有这些都可以通过一次调用获得

struct gpio_descs *gpiod_get_array(struct device *dev,
                                   const char *con_id,
                                   enum gpiod_flags flags)

此函数返回一个包含描述符数组的 struct gpio_descs。它还包含指向 gpiolib 私有结构的指针,如果将该指针传递回 get/set 数组函数,则可能会加速 I/O 处理

struct gpio_descs {
        struct gpio_array *info;
        unsigned int ndescs;
        struct gpio_desc *desc[];
}

如果没有将 GPIO 分配给请求的功能,则以下函数返回 NULL 而不是 -ENOENT

struct gpio_descs *gpiod_get_array_optional(struct device *dev,
                                            const char *con_id,
                                            enum gpiod_flags flags)

还定义了这些函数的设备管理变体

struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
                                 enum gpiod_flags flags)

struct gpio_desc *devm_gpiod_get_index(struct device *dev,
                                       const char *con_id,
                                       unsigned int idx,
                                       enum gpiod_flags flags)

struct gpio_desc *devm_gpiod_get_optional(struct device *dev,
                                          const char *con_id,
                                          enum gpiod_flags flags)

struct gpio_desc *devm_gpiod_get_index_optional(struct device *dev,
                                                const char *con_id,
                                                unsigned int index,
                                                enum gpiod_flags flags)

struct gpio_descs *devm_gpiod_get_array(struct device *dev,
                                        const char *con_id,
                                        enum gpiod_flags flags)

struct gpio_descs *devm_gpiod_get_array_optional(struct device *dev,
                                                 const char *con_id,
                                                 enum gpiod_flags flags)

可以使用 gpiod_put() 函数释放 GPIO 描述符

void gpiod_put(struct gpio_desc *desc)

对于 GPIO 数组,可以使用此函数

void gpiod_put_array(struct gpio_descs *descs)

在调用这些函数后严格禁止使用描述符。也不允许从通过 gpiod_get_array() 获取的数组中单独释放描述符(使用 gpiod_put())。

毫不奇怪,设备管理变体是

void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)

void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)

使用 GPIO

设置方向

驱动程序使用 GPIO 的第一件事是设置其方向。如果未向 gpiod_get*() 提供任何方向设置标志,则通过调用 gpiod_direction_*() 函数之一来完成此操作。

int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)

返回值为零表示成功,否则为负的 errno。应该检查返回值,因为 get/set 调用不会返回错误,并且可能会出现配置错误。您通常应该从任务上下文中发出这些调用。但是,对于自旋锁安全的 GPIO,在启用任务处理之前(作为早期板级设置的一部分)使用它们是可以的。

对于输出 GPIO,提供的值将成为初始输出值。这有助于避免系统启动期间的信号毛刺。

驱动程序还可以查询 GPIO 的当前方向

int gpiod_get_direction(const struct gpio_desc *desc)

此函数返回 0 表示输出,1 表示输入,如果发生错误则返回错误代码。

请注意,GPIO 没有默认方向。因此,在未首先设置其方向的情况下使用 GPIO 是非法的,并且会导致未定义的行为!

自旋锁安全的 GPIO 访问

大多数 GPIO 控制器可以通过内存读/写指令进行访问。这些操作不需要休眠,并且可以安全地在硬中断(非线程)处理程序和类似上下文中完成。

使用以下调用从原子上下文中访问 GPIO

int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);

这些值是布尔值,零表示非活动状态,非零值表示活动状态。读取输出引脚的值时,返回的值应该是引脚上看到的值。由于包括开漏信号和输出延迟在内的问题,这并不总是与指定的输出值匹配。

get/set 调用不返回错误,因为“无效的 GPIO”应该已在之前的 gpiod_direction_*() 中报告。但是,请注意,并非所有平台都可以读取输出引脚的值;那些不能读取的平台应始终返回零。此外,对于不能在不休眠的情况下安全访问的 GPIO(请参见下文)使用这些调用是错误的。

可能休眠的 GPIO 访问

某些 GPIO 控制器必须使用基于消息的总线(如 I2C 或 SPI)进行访问。读取或写入这些 GPIO 值的命令需要等待进入队列头才能发送命令并获取其响应。这需要休眠,而休眠不能在中断处理程序内部完成。

支持此类 GPIO 的平台通过从此调用返回非零值来将它们与其他 GPIO 区分开来

int gpiod_cansleep(const struct gpio_desc *desc)

为了访问此类 GPIO,定义了一组不同的访问器

int gpiod_get_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)

访问此类 GPIO 需要一个可能休眠的上下文,例如线程化的中断处理程序,并且必须使用这些访问器,而不是没有 cansleep() 名称后缀的自旋锁安全访问器。

除了这些访问器可能会休眠,并且可以在无法从硬中断处理程序访问的 GPIO 上工作之外,这些调用的行为与自旋锁安全调用相同。

低电平有效和开漏语义

由于消费者不应关心物理线路电平,因此所有 gpiod_set_value_xxx() 或 gpiod_set_array_value_xxx() 函数都使用逻辑值进行操作。它们会考虑低电平有效属性。这意味着它们会检查 GPIO 是否配置为低电平有效,如果是,它们会在驱动物理线路电平之前操作传递的值。

开漏或开源输出线路也是如此:它们不会主动将其输出驱动为高电平(开漏)或低电平(开源),它们只是将其输出切换到高阻抗值。消费者无需关心。(有关详细信息,请阅读GPIO 驱动程序接口中的开漏。)

有了这个,所有 gpiod_set_(array)_value_xxx() 函数都将参数“value”解释为“活动”(“1”)或“非活动”(“0”)。物理线路电平将相应地被驱动。

例如,如果设置了专用 GPIO 的低电平有效属性,并且 gpiod_set_(array)_value_xxx() 传递了“活动”(“1”),则物理线路电平将被驱动为低电平。

总结

Function (example)                 line property          physical line
gpiod_set_raw_value(desc, 0);      don't care             low
gpiod_set_raw_value(desc, 1);      don't care             high
gpiod_set_value(desc, 0);          default (active high)  low
gpiod_set_value(desc, 1);          default (active high)  high
gpiod_set_value(desc, 0);          active low             high
gpiod_set_value(desc, 1);          active low             low
gpiod_set_value(desc, 0);          open drain             low
gpiod_set_value(desc, 1);          open drain             high impedance
gpiod_set_value(desc, 0);          open source            high impedance
gpiod_set_value(desc, 1);          open source            high

可以使用 set_raw/get_raw 函数来覆盖这些语义,但应尽可能避免这样做,尤其是对于不需要关心实际物理线路电平而应担心逻辑值的系统无关驱动程序而言。

访问原始 GPIO 值

存在一些消费者需要管理 GPIO 线路的逻辑状态,即它们的设备实际接收到的值,无论它和 GPIO 线路之间存在什么。

以下调用集忽略 GPIO 的低电平有效或开漏属性,并处理原始线路值

int gpiod_get_raw_value(const struct gpio_desc *desc)
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)

GPIO 的低电平有效状态也可以使用以下调用进行查询和切换

int gpiod_is_active_low(const struct gpio_desc *desc)
void gpiod_toggle_active_low(struct gpio_desc *desc)

请注意,这些函数应谨慎使用;驱动程序无需关心物理线路电平或开漏语义。

使用单个函数调用访问多个 GPIO

以下函数获取或设置 GPIO 数组的值

int gpiod_get_array_value(unsigned int array_size,
                          struct gpio_desc **desc_array,
                          struct gpio_array *array_info,
                          unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
                              struct gpio_desc **desc_array,
                              struct gpio_array *array_info,
                              unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
                                   struct gpio_desc **desc_array,
                                   struct gpio_array *array_info,
                                   unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
                                   struct gpio_desc **desc_array,
                                   struct gpio_array *array_info,
                                   unsigned long *value_bitmap);

int gpiod_set_array_value(unsigned int array_size,
                          struct gpio_desc **desc_array,
                          struct gpio_array *array_info,
                          unsigned long *value_bitmap)
int gpiod_set_raw_array_value(unsigned int array_size,
                              struct gpio_desc **desc_array,
                              struct gpio_array *array_info,
                              unsigned long *value_bitmap)
int gpiod_set_array_value_cansleep(unsigned int array_size,
                                   struct gpio_desc **desc_array,
                                   struct gpio_array *array_info,
                                   unsigned long *value_bitmap)
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
                                       struct gpio_desc **desc_array,
                                       struct gpio_array *array_info,
                                       unsigned long *value_bitmap)

该数组可以是任意一组 GPIO。如果相应的芯片驱动程序支持,这些函数将尝试同时访问属于同一组或芯片的 GPIO。在这种情况下,可以预期性能会显着提高。如果无法同时访问,则将依次访问 GPIO。

这些函数接受四个参数

  • array_size - 数组元素的数量

  • desc_array - GPIO 描述符数组

  • array_info - 从 gpiod_get_array() 获取的可选信息

  • value_bitmap - 用于存储 GPIO 值的位图(get)或要分配给 GPIO 的值的位图(set)

描述符数组可以使用 gpiod_get_array() 函数或其变体之一获取。如果该函数返回的描述符组与所需的 GPIO 组匹配,则可以通过简单地使用 gpiod_get_array() 返回的 struct gpio_descs 来访问这些 GPIO。

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
                      my_gpio_descs->info, my_gpio_value_bitmap);

也可以访问完全任意的描述符数组。可以使用 gpiod_get()gpiod_get_array() 的任意组合来获取描述符。之后,必须手动设置描述符数组,然后才能将其传递给上述函数之一。在这种情况下,应将 array_info 设置为 NULL。

请注意,为了获得最佳性能,属于同一芯片的 GPIO 应在描述符数组中连续排列。

如果描述符的数组索引与单个芯片的硬件引脚号匹配,则可以实现更好的性能。如果传递给 get/set 数组函数的数组与从 gpiod_get_array() 获取的数组匹配,并且还传递了与该数组关联的 array_info,则该函数可能会采用快速位图处理路径,将 value_bitmap 参数直接传递给芯片的相应 .get/set_multiple() 回调。这允许利用 GPIO 组作为数据 I/O 端口,而不会损失太多性能。

gpiod_get_array_value() 及其变体的返回值为 0 表示成功,否则为负值表示错误。请注意与 gpiod_get_value() 的区别,后者在成功时返回 0 或 1 来传达 GPIO 值。对于数组函数,GPIO 值存储在 value_array 中,而不是作为返回值返回。

映射到 IRQ 的 GPIO

GPIO 线路通常可以用作 IRQ。您可以使用以下调用获取与给定 GPIO 对应的 IRQ 号

int gpiod_to_irq(const struct gpio_desc *desc)

如果无法完成映射(很可能是因为该特定 GPIO 不能用作 IRQ),它将返回 IRQ 号或负的 errno 代码。使用未通过 gpiod_direction_input() 设置为输入的 GPIO,或使用最初不是来自 gpiod_to_irq() 的 IRQ 号是未经检查的错误。gpiod_to_irq() 不允许休眠。

gpiod_to_irq() 返回的非错误值可以传递给 request_irq()free_irq()。它们通常会由特定于板级的初始化代码存储到平台设备的 IRQ 资源中。请注意,IRQ 触发选项是 IRQ 接口的一部分,例如 IRQF_TRIGGER_FALLING,系统唤醒功能也是如此。

GPIO 和 ACPI

在 ACPI 系统上,GPIO 由设备的 _CRS 配置对象列出的 GpioIo()/GpioInt() 资源描述。这些资源不提供 GPIO 的连接 ID(名称),因此需要为此目的使用额外的机制。

符合 ACPI 5.1 或更高版本的系统可以提供 _DSD 配置对象,该对象除其他事项外,可用于为 _CRS 中 GpioIo()/GpioInt() 资源描述的特定 GPIO 提供连接 ID。如果存在这种情况,则将由 GPIO 子系统自动处理。但是,如果 _DSD 不存在,则需要在设备驱动程序中提供 GpioIo()/GpioInt() 资源和 GPIO 连接 ID 之间的映射。

有关详细信息,请参阅与 GPIO 相关的 _DSD 设备属性

与旧版 GPIO 子系统交互

许多内核子系统和驱动程序仍然使用传统的基于整数的接口来处理 GPIO。强烈建议将这些更新到新的 gpiod 接口。对于需要同时使用两种接口的情况,以下两个函数允许将 GPIO 描述符转换为 GPIO 整数命名空间,反之亦然。

int desc_to_gpio(const struct gpio_desc *desc)
struct gpio_desc *gpio_to_desc(unsigned gpio)

desc_to_gpio() 返回的 GPIO 编号可以安全地用作 gpio_*() 函数的参数,只要 GPIO 描述符 desc 没有被释放。同样,传递给 gpio_to_desc() 的 GPIO 编号必须首先使用例如 gpio_request_one() 正确获取,并且返回的 GPIO 描述符仅在 GPIO 编号使用 gpio_free() 释放之前被认为是有效的。

禁止使用一个 API 获取的 GPIO 使用另一个 API 释放,这是一个未检查的错误。