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 子系统将 con_id 中传递的字符串与 GPIO 后缀(“gpios” 或 “gpio”)作为前缀,以获取结果字符串 (snprintf(... "%s-%s", con_id, gpio_suffixes[])。

ACPI

ACPI 也以类似于 DT 的方式支持 GPIO 的函数名称。上面的 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 的硬件编号,或 U16_MAX 表示 key 是 GPIO 线路名称

  • 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 占用表来占用 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 时立即被占用,或者在芯片较早创建的情况下,在注册占用表时被占用。

引脚数组

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

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

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

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

否则,不使用快速位图处理路径,以避免分别处理属于同一芯片但不在硬件顺序中的连续引脚。

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