_DSD 设备属性与 GPIO 的关系¶
随着 ACPI 5.1 的发布,_DSD 配置对象最终允许为 _CRS 返回的 GPIO(以及其他东西)命名。 以前,我们只能使用整数索引来查找对应的 GPIO,这非常容易出错(例如,它取决于 _CRS 输出顺序)。
有了 _DSD,我们现在可以使用名称而不是整数索引来查询 GPIO,如下面的 ASL 示例所示
// 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() 资源中的引脚。通常为零。
- 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 在这里尝试使用常识,并从偏置和极性设置中得出状态。下表显示了预期
拉偏置 |
极性 |
请求... |
---|---|---|
隐式 |
||
默认 |
x |
按原样(假设固件为我们配置了它) |
显式 |
||
无 |
x |
按原样(假设固件为我们配置了它),没有拉偏置 |
上拉 |
x(没有 _DSD) |
作为高电平,假设为非活动状态 |
低电平 |
||
高电平 |
作为高电平,假设为活动状态 |
|
下拉 |
x(没有 _DSD) |
作为低电平,假设为非活动状态 |
高电平 |
||
低电平 |
作为低电平,假设为活动状态 |
也就是说,对于我们上面的示例,由于偏置设置是显式的并且存在 _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 极性被认为是高电平有效。即使在涉及 _DSD() 的情况下(请参阅上面的示例),也必须将 GPIO CS 极性定义为高电平有效,以避免歧义。
其他支持的属性¶
以下设备树兼容设备属性也受 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 表(通常在 DSDT 内)的 GPIO 控制器声明中。 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,但提供带有 GpioIo()/GpioInt() 资源的 _CRS,并且设备驱动程序仍然需要使用它们。
在这些情况下,驱动程序可以使用 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() 资源的索引(从零开始)、该资源中目标行的索引(从零开始)以及该行的 active-low 标志,与上面指定的 _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_dev_remove_driver_gpios() 删除先前注册到 ACPI 设备对象的 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...
但是,由于无法知道 “reset” 和 _CRS 中的 GpioIo() 之间的映射,desc 将包含 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);
我们可以考虑两种不同的情况,即提供连接 ID 和不提供连接 ID。
情况 1
desc = gpiod_get(dev, "non-null-connection-id", flags);
desc = gpiod_get_index(dev, "non-null-connection-id", index, flags);
情况 2
desc = gpiod_get(dev, NULL, flags);
desc = gpiod_get_index(dev, NULL, index, flags);
情况 1 假设相应的 ACPI 设备描述必须定义设备属性,否则将阻止获取任何 GPIO 资源。
情况 2 显式告诉 GPIO 核心在 _CRS 中查找资源。
请注意,假设提供了两个版本的 ACPI 设备描述并且驱动程序中不存在映射,gpiod_get_index()
在情况 1 和 2 中将返回不同的资源。这就是为什么某些驱动程序必须像上一章中解释的那样仔细处理它们的原因。