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 的函数,可以使用一次调用来获取所有这些 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 控制器都可以使用内存读取/写入指令进行访问。 这些不需要休眠,并且可以安全地从硬(非线程)IRQ 处理程序和类似上下文中完成。

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

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

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

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

可能休眠的 GPIO 访问

必须使用基于消息的总线(如 I2C 或 SPI)来访问某些 GPIO 控制器。 读取或写入这些 GPIO 值的命令需要等待到达队列的头部以传输命令并获取其响应。 这需要休眠,而这无法从 IRQ 处理程序内部完成。

支持此类型 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 需要可能休眠的上下文,例如线程化的 IRQ 处理程序,并且必须使用这些访问器而不是没有 cansleep() 名称后缀的自旋锁安全访问器。

除了这些访问器可能会休眠并且可以在无法从 hardIRQ 处理程序访问的 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)

它将返回一个 IRQ 编号,如果无法完成映射,则返回一个负的 errno 代码(最可能的原因是该特定 GPIO 无法用作 IRQ)。 使用未设置为使用 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)

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

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