_DSD 与 GPIO 相关的设备属性

随着 ACPI 5.1 的发布,_DSD 配置对象终于允许为 _CRS 返回的 GPIO(以及其他内容)指定名称。以前,我们只能使用整数索引来查找相应的 GPIO,这很容易出错(例如,这取决于 _CRS 输出顺序)。

使用 _DSD,我们现在可以使用名称而不是整数索引来查询 GPIO,如下面的 ASL(ACPI Source Language) 示例所示:

// Bluetooth device with reset and shutdown GPIOs
Device (BTH)
{
    Name (_HID, ...)

    Name (_CRS, ResourceTemplate ()
    {
        GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly,
                "\\_SB.GPO0", 0, ResourceConsumer) { 15 }
        GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly,
                "\\_SB.GPO0", 0, ResourceConsumer) { 27, 31 }
    })

    Name (_DSD, Package ()
    {
        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
        Package ()
        {
            Package () { "reset-gpios", Package () { ^BTH, 1, 1, 0 } },
            Package () { "shutdown-gpios", Package () { ^BTH, 0, 0, 0 } },
        }
    })
}

支持的GPIO属性的格式为:

Package () { "name", Package () { ref, index, pin, active_low }}
  • ref

  具有包含 GpioIo()/GpioInt() 资源的 _CRS 的设备,通常是设备本身(在我们的例子中是 BTH)。

  • index

  _CRS 中 GpioIo()/GpioInt() 资源的索引从零开始。

  • pin

  GpioIo()/GpioInt() 资源中的 Pin。通常为零。

  • active_low

  如果为 1,则 GPIO 标记为 active_low。

由于 ACPI GpioIo() 资源没有字段说明它是低电平有效还是高电平有效,因此可以在此处使用“active_low”参数。将其设置为 1 表示 GPIO 为低电平有效。

请注意,_DSD 中的 active_low 对于 GpioInt() 资源没有意义,必须为 0。GpioInt() 资源有自己的定义方法。

在我们的蓝牙示例中,“reset-gpios”指的是第二个 GpioIo() 资源,即该资源中的第二个引脚,GPIO 编号为 31。

遗憾的是,GpioIo() 资源没有明确提供驱动程序在初始化期间应使用的输出引脚的初始状态。

Linux 尝试在此使用常识,并从偏差和极性设置中得出状态。下表显示了预期结果:

 

也就是说,对于我们上面的例子,由于偏置设置是显式的并且存在 _DSD,因此两个 GPIO 将被视为具有高极性的活动状态,并且 Linux 将在此状态下配置引脚,直到驱动程序对它们进行不同的重新编程。

可以在 GPIO 阵列中留下空洞 。这在 SPI 主机控制器等情况下很有用,因为有些芯片选择可能实现为 GPIO,而有些芯片选择实现为本机信号。例如,SPI 主机控制器可以将芯片选择 0 和 2 实现为 GPIO,将 1 实现为本机:

Package () {
    "cs-gpios",
    Package () {
        ^GPIO, 19, 0, 0, // chip select 0: GPIO
        0,               // chip select 1: native signal
        ^GPIO, 20, 0, 0, // chip select 2: GPIO
    }
}

请注意,ACPI 历史上没有 GPIO 极性的方法,因此 SPISerialBus() 资源在每个芯片的基础上定义它。为了避免一连串的否定,GPIO 极性被认为是 Active High。即使在涉及 _DSD() 的情况下(参见上面的示例),GPIO CS 极性也必须定义为 Active High 以避免歧义。

其他支持的属性

GPIO 控制器的 _DSD 设备属性也支持以下与设备树兼容的设备属性:

  • gpio-hog

  • output-high

  • output-low

  • input

  • line-name

例子:

Name (_DSD, Package () {
    // _DSD Hierarchical Properties Extension UUID
    ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
    Package () {
        Package () { "hog-gpio8", "G8PU" }
    }
})

Name (G8PU, Package () {
    ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
    Package () {
        Package () { "gpio-hog", 1 },
        Package () { "gpios", Package () { 8, 0 } },
        Package () { "output-high", 1 },
        Package () { "line-name", "gpio8-pullup" },
    }
})
  • gpio-line-names

gpio-line-names 声明是一个字符串列表(“名称”),用于描述 GPIO 控制器/扩展器的每个线路/引脚。此列表包含在包中,必须插入 ACPI 表的 GPIO 控制器声明中(通常在 DSDT 内)。gpio-line-names 列表必须遵守以下规则(另请参阅示例):

  • 列表中的第一个名称与 GPIO 控制器/扩展器的第一条线路/引脚相对应
  • 列表中的名称必须是连续的(不允许有“空洞”)
  • 列表可以是不完整的,并且可以在最后一个 GPIO 线路之前结束:换句话说,不必填写所有 GPIO 线路
  • 允许使用空名称(两个引号“”对应一个空名称)
  • 一个 GPIO 控制器/扩展器内的名称必须是唯一的

16 条线的 GPIO 控制器示例,其中包含两个空名称的不完整列表:

Package () {
    "gpio-line-names",
    Package () {
        "pin_0",
        "pin_1",
        "",
        "",
        "pin_3",
        "pin_4_push_button",
    }
}

在运行时,上述声明产生以下结果(使用“libgpiod”工具):

root@debian:~# gpioinfo gpiochip4
gpiochip4 - 16 lines:
        line   0:      "pin_0"       unused   input  active-high
        line   1:      "pin_1"       unused   input  active-high
        line   2:      unnamed       unused   input  active-high
        line   3:      unnamed       unused   input  active-high
        line   4:      "pin_3"       unused   input  active-high
        line   5: "pin_4_push_button" unused input active-high
        line   6:      unnamed       unused   input  active-high
        line   7       unnamed       unused   input  active-high
        line   8:      unnamed       unused   input  active-high
        line   9:      unnamed       unused   input  active-high
        line  10:      unnamed       unused   input  active-high
        line  11:      unnamed       unused   input  active-high
        line  12:      unnamed       unused   input  active-high
        line  13:      unnamed       unused   input  active-high
        line  14:      unnamed       unused   input  active-high
        line  15:      unnamed       unused   input  active-high
root@debian:~# gpiofind pin_4_push_button
gpiochip4 5
root@debian:~#

另一个例子:

Package () {
    "gpio-line-names",
    Package () {
        "SPI0_CS_N", "EXP2_INT", "MUX6_IO", "UART0_RXD",
        "MUX7_IO", "LVL_C_A1", "MUX0_IO", "SPI1_MISO",
    }
}

有关这些属性的更多信息,请参阅 Documentation/devicetree/bindings/gpio/gpio.txt。

驱动程序提供的 ACPI GPIO 映射

在某些系统中,ACPI 表不包含 _DSD,但为 _CRS 提供 GpioIo()/GpioInt() 资源,设备驱动程序仍然需要使用它们。

在这些情况下,驱动程序可用的 ACPI 设备标识对象 _HID、_CID、_CLS、_SUB、_HRV 可用于识别设备,这应该足以确定 _CRS 返回的 GpioIo()/GpioInt() 资源列出的所有 GPIO 线路的含义和用途。换句话说,一旦驱动程序识别了设备,它应该知道 GpioIo()/GpioInt() 资源的用途。完成此操作后,它可以简单地为要使用的 GPIO 线路分配名称,并为 GPIO 子系统提供这些名称与它们对应的 ACPI GPIO 资源之间的映射。

为此,驱动程序需要将映射表定义为以 NULL 结尾的 struct acpi_gpio_mapping 对象数组,每个对象包含一个名称、一个指向线路数据 (struct acpi_gpio_params) 对象数组的指针以及该数组的大小。每个 struct acpi_gpio_params 对象由三个字段组成,crs_entry_index、line_index、active_low,分别表示 _CRS 中目标 GpioIo()/GpioInt() 资源从零开始的索引、该资源中目标线路从零开始的索引以及该线路的低电平有效标志,与上面指定的 _DSD GPIO 属性格式类似。

对于前面讨论的示例蓝牙设备,所涉及的数据结构将如下所示:

static const struct acpi_gpio_params reset_gpio = { 1, 1, false };
static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false };

static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = {
  { "reset-gpios", &reset_gpio, 1 },
  { "shutdown-gpios", &shutdown_gpio, 1 },
  { }
};

接下来,需要将映射表作为第二个参数传递给 acpi_dev_add_driver_gpios() 或其托管模拟,后者会将其注册到其第一个参数指向的 ACPI 设备对象中。这应该在驱动程序的 .probe() 例程中完成。在移除时,驱动程序应该通过在之前注册该表的 ACPI 设备对象上调用 acpi_dev_remove_driver_gpios() 来取消注册其 GPIO 映射表。

使用 _CRS 后备

如果设备没有 _DSD 或驱动程序未创建 ACPI GPIO 映射,Linux GPIO 框架将拒绝返回任何 GPIO。这是因为驱动程序不知道它实际得到的是什么。例如,如果我们有如下所示的设备:

Device (BTH)
{
    Name (_HID, ...)

    Name (_CRS, ResourceTemplate () {
        GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone,
                "\\_SB.GPO0", 0, ResourceConsumer) { 15 }
        GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone,
                "\\_SB.GPO0", 0, ResourceConsumer) { 27 }
    })
}

驱动程序可能期望在以下情况下获得正确的 GPIO:

desc = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(desc))
      ...error handling...

但由于无法知道“重置”与 _CRS desc 中的 GpioIo() 之间的映射,因此将保留 ERR_PTR(-ENOENT)。

驱动程序作者可以通过显式传递映射来解决这个问题(这是推荐的方法,并在上一章中有记录)。

ACPI GPIO 映射表不应污染不知道它们正在为哪个设备提供服务的驱动程序。 这意味着 ACPI GPIO 映射表几乎不与 ACPI ID 和上一章中列出的相关设备的某些对象相关联。

获取GPIO描述符

从 ACPI 获取 GPIO 资源主要有两种方法:

desc = gpiod_get(dev, connection_id, flags);
desc = gpiod_get_index(dev, connection_id, index, flags);

我们可以在这里考虑两种不同的情况,即提供connection ID 的情况和其他情况。

Case 1:

desc = gpiod_get(dev, "non-null-connection-id", flags);
desc = gpiod_get_index(dev, "non-null-connection-id", index, flags);

Case 2:

desc = gpiod_get(dev, NULL, flags);
desc = gpiod_get_index(dev, NULL, index, flags);

情况 1 假设相应的 ACPI 设备描述必须具有已定义的设备属性,否则将阻止获取任何 GPIO 资源。

情况 2 明确告诉 GPIO 核心在 _CRS 中查找资源。

请注意,假设提供了两个版本的 ACPI 设备描述并且驱动程序中没有映射,情况 1 和 2 中的 gpiod_get_index() 将返回不同的资源。这就是为什么某个驱动程序必须小心处理它们,如上一章所述。

 

posted @ 2025-03-20 18:30  闹闹爸爸  阅读(128)  评论(0)    收藏  举报