GPIO描述符消费者接口
本文档描述了GPIO框架的消费者接口。请注意,它描述了新的基于描述符的接口。有关已弃用的基于整数的GPIO接口的描述,请参阅“Legacy GPIO Interfaces”。
GPIO消费者的指南
不能在没有标准GPIO调用的情况下工作的驱动程序应该具有依赖于GPIOLIB的Kconfig条目或选择GPIOLIB。允许驱动程序获取和使用GPIO的函数可通过包含以下文件来使用:
#include <linux/gpio/consumer.h>
在禁用GPIOLIB时,头文件中的所有函数都有静态内联存根。当调用这些存根时,它们将发出警告。这些存根用于两种用例:
- 通过例如COMPILE_TEST进行简单的编译覆盖——当前平台是否启用或选择GPIOLIB并不重要,因为我们不会执行系统。
- 真正可选的GPIOLIB支持——在某些编译时配置的某些系统上,驱动程序实际上并不使用GPIO,但在其他编译时配置下会使用它。在这种情况下,消费者必须确保不调用这些函数,否则用户将收到可能被视为威胁的控制台警告。
所有使用基于描述符的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 Mappings。
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描述符,或者可以使用IS_ERR()检查的错误代码(它们永远不会返回NULL指针)。如果设备/功能/索引三元组没有分配GPIO,则将返回-ENOENT,其他错误代码用于在尝试获取GPIO时发生错误的情况。这对于区分纯粹的错误和可选GPIO参数的缺失是有用的。对于GPIO是可选的常见模式,可以使用gpiod_get_optional()和gpiod_get_index_optional()函数。如果没有将任何GPIO分配给请求的功能,则这些函数返回NULL:
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支持时也返回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:
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。应该检查返回值,因为获取/设置调用不会返回错误,并且可能存在配置错误。通常应该从任务上下文中发出这些调用。但是,对于自旋锁安全的GPIO,在任务启用之前使用它们作为早期板设置的一部分是可以的。
对于输出GPIO,提供的值将成为初始输出值。这有助于避免系统启动期间的信号抖动。
驱动程序还可以查询GPIO的当前方向:
int gpiod_get_direction(const struct gpio_desc *desc)
此函数在错误情况下返回0表示输出,1表示输入,或错误代码。
请注意,GPIO没有默认方向。因此,在未设置其方向的情况下使用GPIO是非法的,并将导致未定义的行为!
GPIO访问和控制
在原子上下文中访问GPIO
大多数GPIO控制器可以通过内存读/写指令进行访问。这些操作不需要休眠,并且可以安全地在硬件(非线程化)中断处理程序和类似上下文中执行。
使用以下调用来从原子上下文中访问GPIO:
int gpiod_get_value(const struct gpio_desc *desc); void gpiod_set_value(struct gpio_desc *desc, int value);
这些值是布尔值,对于低电平为零,对于高电平为非零。当读取输出引脚的值时,返回的值应该是引脚上看到的值。这不总是与指定的输出值匹配,这是因为存在开漏信号和输出延迟等问题。
这些获取/设置调用不会返回错误,因为“无效的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()后缀的访问器,而不是spinlock-safe访问器。
除了这些访问器可能会休眠,并且将在不能从hardIRQ处理程序中访问的GPIO上工作之外,这些调用与spinlock-safe调用的行为相同。
活动低电平和开漏语义
由于消费者不必关心物理线路电平,所有的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"),那么物理线路电平将被拉低。
访问原始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,则将顺序访问这些GPIO。
这些函数接受四个参数:
- array_size - 数组元素的数量
- desc_array - GPIO描述符的数组
- array_info - 从gpiod_get_array()获取的可选信息
- value_bitmap - 用于存储GPIO值(获取)或要分配给GPIO的值的位图(设置)
描述符数组可以使用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);
也可以访问一组完全任意的描述符。然后,必须手动设置描述符数组,然后才能将其传递给上述函数。在这种情况下,array_info应设置为NULL。
请注意,为了获得最佳性能,属于同一芯片的GPIO应在描述符数组中是连续的。
如果描述符的数组索引与单个芯片的硬件引脚号匹配,可能会获得更好的性能。如果传递给get/set数组函数的数组与从gpiod_get_array()获取的数组匹配,并且与数组相关联的array_info也被传递,那么函数可能会采用快速位图处理路径,直接将value_bitmap参数传递给芯片的相应.get/set_multiple()回调。这允许利用GPIO bank作为数据I/O端口,而不会丢失太多性能。
gpiod_get_array_value()及其变体的返回值在成功时为0,出错时为负值。请注意,与gpiod_get_value()的返回值不同,后者在成功时返回0或1以传达GPIO值。对于数组函数,GPIO值存储在value_array中,而不是作为返回值传回。
将GPIO映射到IRQ
GPIO线通常可以用作IRQ。可以使用以下调用获取与给定GPIO对应的IRQ号:
int gpiod_to_irq(const struct gpio_desc *desc)
它将返回一个IRQ号,或者如果无法进行映射(很可能是因为该特定GPIO不能用作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号后才被认为是有效的。
使用一个API获取的GPIO,然后使用另一个API释放,是被禁止的,是一个未经检查的错误。