_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 中將返回不同的資源。 這就是為什麼某個驅動程式必須按照上一章中的說明謹慎處理它們的原因。