_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() 资源有自己的定义方式。
在我们的 Bluetooth 示例中,“reset-gpios”指的是第二个 GpioIo() 资源,该资源中的第二个引脚,GPIO 编号为 31。
不幸的是,GpioIo() 资源没有明确提供驱动程序在其初始化期间应使用的输出引脚的初始状态。
Linux 尝试在此处使用常识,并从偏置和极性设置中派生状态。 下表显示了期望
拉偏置 |
极性 |
请求的... |
---|---|---|
隐式 |
||
默认 |
x |
AS IS(假设固件为我们配置了它) |
显式 |
||
无 |
x |
AS IS(假设固件为我们配置了它)没有拉偏置 |
向上 |
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 表的 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,但提供带 GpioIo()/GpioInt() 资源的 _CRS,设备驱动程序仍然需要使用它们。
在这些情况下,驱动程序可以使用 ACPI 设备识别对象 _HID、_CID、_CLS、_SUB、_HRV 来识别设备,这应该足以确定 _CRS 返回的 GpioIo()/GpioInt() 资源中列出的所有 GPIO 线的含义和用途。 换句话说,驱动程序应该知道在识别设备后 GpioIo()/GpioInt() 资源用于什么。 完成此操作后,它可以简单地为它将要使用的 GPIO 线分配名称,并向 GPIO 子系统提供这些名称与对应于它们的 ACPI GPIO 资源之间的映射。
为此,驱动程序需要将映射表定义为 struct acpi_gpio_mapping 对象的 NULL 终止数组,每个对象都包含一个名称、一个指向线数据数组(struct acpi_gpio_params)对象的指针以及该数组的大小。 每个 struct acpi_gpio_params 对象由三个字段组成:crs_entry_index、line_index、active_low,分别表示 _CRS 中目标 GpioIo()/GpioInt() 资源(从零开始)的索引、该资源中目标线(从零开始)的索引以及该线的低电平有效标志,这与上面指定的 _DSD GPIO 属性格式类似。
对于先前讨论的 Bluetooth 设备示例,相关的数据结构将如下所示
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...
但是由于无法知道“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 时,否则。
情况 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 中将返回不同的资源。 这就是为什么某个驱动程序必须按照上一章中的说明谨慎处理它们的原因。