GPIO映射 【ChatGPT】

发布时间 2023-12-11 15:57:09作者: 摩斯电码

GPIO映射

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

请注意,这仅适用于基于新描述符的接口。有关已弃用的基于整数的GPIO接口的描述,请参阅“Legacy GPIO Interfaces”(实际上,使用旧接口无法进行真正的映射;您只是从某个地方获取一个整数,然后请求相应的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)将返回true)。

gpiod_get()函数的第二个参数,即con_id字符串,必须是GPIO后缀(由gpiod函数内部自动查找的"gpios"或"gpio")的<function>-前缀。使用上述"led-gpios"示例,使用不带"-"的前缀作为con_id参数:"led"。

在内部,GPIO子系统使用传递给con_id的字符串和GPIO后缀("gpios"或"gpio")的字符串进行前缀(snprintf(... "%s-%s", con_id, gpio_suffixes[]))。

ACPI

ACPI也支持类似于设备树的GPIO功能名称。上述设备树示例可以通过_DSD(设备特定数据)转换为等效的ACPI描述,引入自ACPI 5.1:

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线是瞬态的,在挂起/恢复期间可能失去其电气状态

将查找表定义如下,使用空条目定义其结束。表的'dev_id'字段是将使用这些GPIO的设备的标识符。它可以为NULL,在这种情况下将匹配对gpiod_get()的调用。

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()回调之间传递。

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

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

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

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

希望了解更多信息,可以参考原文。