GPIO 映射

本文档解释了如何将 GPIO 分配给给定的设备和功能。

所有平台都可以启用 GPIO 库,但如果平台严格要求存在 GPIO 功能,则需要从其 Kconfig 中选择 GPIOLIB。然后,GPIO 的映射方式取决于平台使用什么来描述其硬件布局。目前,可以通过设备树、ACPI 和平台数据来定义映射。

设备树

在设备树中,GPIO 可以很容易地映射到设备和功能。确切的做法取决于提供 GPIO 的 GPIO 控制器,请参阅控制器的设备树绑定。

GPIO 映射在消费者设备的节点中定义,位于名为 <function>-gpios 的属性中,其中 <function> 是驱动程序将通过 gpiod_get() 请求的功能。例如

foo_device {
        compatible = "acme,foo";
        ...
        led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
                    <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
                    <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */

        power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
};

名为 <function>-gpio 的属性也被认为是有效的,旧的绑定使用它,但仅出于兼容性原因才支持它,不应将其用于较新的绑定,因为它已被弃用。

此属性将使 GPIO 15、16 和 17 在驱动程序中以“led”功能提供,并将 GPIO 1 作为“power” GPIO 提供

struct gpio_desc *red, *green, *blue, *power;

red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);

power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);

led GPIO 将是高电平有效,而 power GPIO 将是低电平有效(即 gpiod_is_active_low(power) 将为真)。

gpiod_get() 函数的第二个参数,con_id 字符串,必须是设备树中使用的 GPIO 后缀(“gpios”或“gpio”,由 gpiod 函数在内部自动查找)的 <function>- 前缀。 对于上面的 “led-gpios” 示例,请使用不带 “-” 的前缀作为 con_id 参数:“led”。

在内部,GPIO 子系统将 GPIO 后缀(“gpios”或“gpio”)与 con_id 中传递的字符串作为前缀,以获得结果字符串 (snprintf(... "%s-%s", con_id, gpio_suffixes[])。

ACPI

ACPI 还支持 GPIO 的功能名称,方式与 DT 类似。 上面的 DT 示例可以在 ACPI 5.1 中引入的 _DSD(设备特定数据)的帮助下转换为等效的 ACPI 描述

Device (FOO) {
        Name (_CRS, ResourceTemplate () {
                GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly,
                        "\\_SB.GPI0", 0, ResourceConsumer) { 15 } // red
                GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly,
                        "\\_SB.GPI0", 0, ResourceConsumer) { 16 } // green
                GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly,
                        "\\_SB.GPI0", 0, ResourceConsumer) { 17 } // blue
                GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionOutputOnly,
                        "\\_SB.GPI0", 0, ResourceConsumer) { 1 } // power
        })

        Name (_DSD, Package () {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package () {
                        Package () {
                                "led-gpios",
                                Package () {
                                        ^FOO, 0, 0, 1,
                                        ^FOO, 1, 0, 1,
                                        ^FOO, 2, 0, 1,
                                }
                        },
                        Package () { "power-gpios", Package () { ^FOO, 3, 0, 0 } },
                }
        })
}

有关 ACPI GPIO 绑定的更多信息,请参阅 与 GPIO 相关的 _DSD 设备属性

平台数据

最后,可以使用平台数据将 GPIO 绑定到设备和功能。 希望这样做的板文件需要包含以下头文件

#include <linux/gpio/machine.h>

GPIO 通过查找表进行映射,查找表包含 gpiod_lookup 结构的实例。 定义了两个宏来帮助声明此类映射

GPIO_LOOKUP(key, chip_hwnum, con_id, flags)
GPIO_LOOKUP_IDX(key, chip_hwnum, con_id, idx, flags)

其中

  • key 是提供 GPIO 的 gpiod_chip 实例的标签,或者是 GPIO 线路名称

  • chip_hwnum 是芯片中 GPIO 的硬件编号,如果 key 是 GPIO 线路名称,则为 U16_MAX

  • con_id 是从设备角度来看的 GPIO 功能的名称。 它

    可以为 NULL,在这种情况下,它将匹配任何功能。

  • idx 是功能中 GPIO 的索引。

  • 定义 flags 以指定以下属性
    • GPIO_ACTIVE_HIGH - GPIO 线路为高电平有效

    • GPIO_ACTIVE_LOW - GPIO 线路为低电平有效

    • GPIO_OPEN_DRAIN - GPIO 线路设置为开漏

    • GPIO_OPEN_SOURCE - GPIO 线路设置为开源

    • GPIO_PERSISTENT - GPIO 线路在

      挂起/恢复期间保持不变并保持其值

    • GPIO_TRANSITORY - GPIO 线路是短暂的,并且可能失去其

      在挂起/恢复期间的电气状态

将来,这些标志可能会扩展以支持更多属性。

请注意
  1. GPIO 线路名称不能保证是全局唯一的,因此将使用找到的第一个匹配项。

  2. GPIO_LOOKUP() 只是 GPIO_LOOKUP_IDX() 的快捷方式,其中 idx = 0。

然后可以按如下方式定义查找表,其中一个空条目定义了其结尾。 该表的“dev_id”字段是使用这些 GPIO 的设备的标识符。 它可以为 NULL,在这种情况下,它将与对 gpiod_get() 的调用匹配,其中设备为 NULL。

struct gpiod_lookup_table gpios_table = {
        .dev_id = "foo.0",
        .table = {
                GPIO_LOOKUP_IDX("gpio.0", 15, "led", 0, GPIO_ACTIVE_HIGH),
                GPIO_LOOKUP_IDX("gpio.0", 16, "led", 1, GPIO_ACTIVE_HIGH),
                GPIO_LOOKUP_IDX("gpio.0", 17, "led", 2, GPIO_ACTIVE_HIGH),
                GPIO_LOOKUP("gpio.0", 1, "power", GPIO_ACTIVE_LOW),
                { },
        },
};

并且可以按如下方式将表添加到板代码中

gpiod_add_lookup_table(&gpios_table);

然后,控制“foo.0”的驱动程序可以按如下方式获取其 GPIO

struct gpio_desc *red, *green, *blue, *power;

red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);

power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);

由于“led”GPIO 映射为高电平有效,因此此示例会将它们的信号切换为 1,即启用 LED。 对于映射为低电平有效的“power”GPIO,此代码后的实际信号将为 0。 与传统的整数 GPIO 接口相反,低电平有效属性在映射期间处理,因此对 GPIO 使用者是透明的。

诸如 gpiod_set_value() 之类的一组函数可用于使用新的面向描述符的接口。

使用平台数据的板还可以通过定义 GPIO hog 表来占用 GPIO 线路。

struct gpiod_hog gpio_hog_table[] = {
        GPIO_HOG("gpio.0", 10, "foo", GPIO_ACTIVE_LOW, GPIOD_OUT_HIGH),
        { }
};

并且可以按如下方式将表添加到板代码中

gpiod_add_hogs(gpio_hog_table);

一旦创建 gpiochip,或者 - 如果芯片是较早创建的 - 当注册 hog 表时,该线路将被占用。

引脚数组

除了逐个请求属于某个功能的引脚之外,设备还可以请求分配给该功能的引脚数组。 这些引脚映射到设备的方式决定了该数组是否符合快速位图处理的条件。 如果符合条件,则位图会在调用者和 GPIO 芯片的相应 .get/set_multiple() 回调之间直接通过 get/set 数组函数传递。

为了符合快速位图处理的条件,该数组必须满足以下要求

  • 数组成员 0 的引脚硬件编号也必须为 0,

  • 属于与成员 0 相同的芯片的连续数组成员的引脚硬件编号也必须与其数组索引匹配。

否则,不使用快速位图处理路径,以避免属于同一芯片但不按硬件顺序排列的连续引脚被单独处理。

如果该数组适用于快速位图处理路径,则属于与成员 0 不同的芯片的引脚,以及索引与硬件引脚编号不同的引脚,都将从快速路径中排除,无论是输入还是输出。 此外,开漏和开源引脚将从快速位图输出处理中排除。