GPIO 描述符消費者介面¶
本文件描述了 GPIO 框架的消費者介面。
GPIO 消費者指南¶
不能在沒有標準 GPIO 呼叫的情況下工作的驅動程式應該具有依賴於 GPIOLIB 或選擇 GPIOLIB 的 Kconfig 條目。 透過包含以下檔案,可以使用允許驅動程式獲取和使用 GPIO 的函式
#include <linux/gpio/consumer.h>
如果停用 GPIOLIB,則標頭檔案中的所有函式都有靜態內聯存根。 當呼叫這些存根時,它們將發出警告。 這些存根用於兩個用例
簡單的編譯覆蓋,例如 COMPILE_TEST - 當前平臺是否啟用或選擇 GPIOLIB 並不重要,因為無論如何我們都不會執行該系統。
真正的可選 GPIOLIB 支援 - 在某些系統的某些編譯時配置中,驅動程式實際上並不使用 GPIO,但在其他編譯時配置下將使用它。 在這種情況下,消費者必須確保不呼叫這些函式,否則使用者將遇到可能被認為具有威脅性的控制檯警告。 將真正可選的 GPIOLIB 用法與呼叫
[devm_]gpiod_get_optional()結合使用是一個壞主意,並且會導致奇怪的錯誤訊息。 將可選 GPIOLIB 與普通的 getter 函式一起使用:當您這樣做時,應該預料到一些開放式的錯誤處理。
所有使用基於描述符的 GPIO 介面的函式都以 gpiod_ 作為字首。 gpio_ 字首用於舊版介面。 核心中的任何其他函式都不應使用這些字首。 強烈建議不要使用舊版函式,新程式碼應專門使用 <linux/gpio/consumer.h> 和描述符。
獲取和釋放 GPIO¶
使用基於描述符的介面,GPIO 透過不透明的、不可偽造的處理程式來標識,該處理程式必須透過呼叫 gpiod_get() 函式之一來獲取。 與許多其他核心子系統一樣,gpiod_get() 採用將使用 GPIO 的裝置和所請求的 GPIO 應該履行的功能
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
如果一個函式是透過一起使用多個 GPIO 來實現的(例如,一個簡單的 LED 裝置,用於顯示數字),則可以指定一個額外的索引引數
struct gpio_desc *gpiod_get_index(struct device *dev,
const char *con_id, unsigned int idx,
enum gpiod_flags flags)
有關 DeviceTree 情況下 con_id 引數的更詳細描述,請參閱 GPIO 對映
flags 引數用於可選地指定 GPIO 的方向和初始值。 值可以是
GPIOD_ASIS 或 0 以完全不初始化 GPIO。 方向必須稍後使用其中一個專用函式進行設定。
GPIOD_IN 將 GPIO 初始化為輸入。
GPIOD_OUT_LOW 將 GPIO 初始化為輸出,值為 0。
GPIOD_OUT_HIGH 將 GPIO 初始化為輸出,值為 1。
GPIOD_OUT_LOW_OPEN_DRAIN 與 GPIOD_OUT_LOW 相同,但也強制該線路在電氣上與開漏一起使用。
GPIOD_OUT_HIGH_OPEN_DRAIN 與 GPIOD_OUT_HIGH 相同,但也強制該線路在電氣上與開漏一起使用。
請注意,初始值是邏輯值,物理線路電平取決於線路是配置為高電平有效還是低電平有效(請參閱 低電平有效和開漏語義)。
最後兩個標誌用於開漏是強制性的用例,例如 I2C:如果線路尚未在對映中配置為開漏(請參閱 GPIO 對映),則無論如何都會強制使用開漏,並且會列印一條警告,指出需要更新板配置以匹配用例。
這兩個函式都返回有效的 GPIO 描述符,或者返回一個可以使用 IS_ERR() 檢查的錯誤程式碼(它們永遠不會返回 NULL 指標)。 僅當且僅當沒有將 GPIO 分配給裝置/功能/索引三元組時,才會返回 -ENOENT,其他錯誤程式碼用於已分配 GPIO 但嘗試獲取時發生錯誤的情況。 這對於區分僅僅的錯誤和可選 GPIO 引數缺少 GPIO 非常有用。 對於 GPIO 可選的常見模式,可以使用 gpiod_get_optional() 和 gpiod_get_index_optional() 函式。 如果沒有將 GPIO 分配給請求的函式,這些函式將返回 NULL 而不是 -ENOENT
struct gpio_desc *gpiod_get_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
struct gpio_desc *gpiod_get_index_optional(struct device *dev,
const char *con_id,
unsigned int index,
enum gpiod_flags flags)
請注意,gpio_get*_optional() 函式(及其託管變體),與 gpiolib API 的其餘部分不同,當停用 gpiolib 支援時,也會返回 NULL。 這對驅動程式作者很有幫助,因為他們不需要專門處理 -ENOSYS 返回程式碼。 但是,系統整合商應注意在需要它的系統上啟用 gpiolib。
對於使用多個 GPIO 的函式,可以使用一次呼叫來獲取所有這些 GPIO
struct gpio_descs *gpiod_get_array(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
此函式返回一個 struct gpio_descs,其中包含一個描述符陣列。 它還包含一個指向 gpiolib 私有結構的指標,如果將其傳遞迴 get/set 陣列函式,則可能會加快 I/O 處理
struct gpio_descs {
struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
}
如果未將 GPIO 分配給請求的函式,則以下函式將返回 NULL 而不是 -ENOENT
struct gpio_descs *gpiod_get_array_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
這些函式的裝置託管變體也已定義
struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_index_optional(struct device *dev,
const char *con_id,
unsigned int index,
enum gpiod_flags flags)
struct gpio_descs *devm_gpiod_get_array(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
struct gpio_descs *devm_gpiod_get_array_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
可以使用 gpiod_put() 函式釋放 GPIO 描述符
void gpiod_put(struct gpio_desc *desc)
對於 GPIO 陣列,可以使用此函式
void gpiod_put_array(struct gpio_descs *descs)
在呼叫這些函式後,嚴禁使用描述符。 也不允許從使用 gpiod_get_array() 獲取的陣列中單獨釋放描述符(使用 gpiod_put())。
不出所料,裝置託管變體是
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)
使用 GPIO¶
設定方向¶
驅動程式必須對 GPIO 做的第一件事是設定其方向。 如果沒有為 gpiod_get*() 提供任何方向設定標誌,則透過呼叫 gpiod_direction_*() 函式之一來完成此操作
int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)
返回值成功時為零,否則為負 errno。 應該檢查它,因為 get/set 呼叫不返回錯誤,並且可能會出現配置錯誤。 通常,您應該從任務上下文中發出這些呼叫。 但是,對於自旋鎖安全的 GPIO,在啟用任務處理之前,作為早期板設定的一部分,可以使用它們是可以的。
對於輸出 GPIO,提供的值將成為初始輸出值。 這有助於避免系統啟動期間的訊號毛刺。
驅動程式還可以查詢 GPIO 的當前方向
int gpiod_get_direction(const struct gpio_desc *desc)
此函式對於輸出返回 0,對於輸入返回 1,如果發生錯誤,則返回錯誤程式碼。
請注意,GPIO 沒有預設方向。 因此,在未先設定 GPIO 方向的情況下使用 GPIO 是非法的,並且會導致未定義的行為!
自旋鎖安全的 GPIO 訪問¶
大多數 GPIO 控制器都可以使用記憶體讀取/寫入指令進行訪問。 這些不需要休眠,並且可以安全地從硬(非執行緒)IRQ 處理程式和類似上下文中完成。
使用以下呼叫從原子上下文中訪問 GPIO
int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);
這些值是布林值,非活動時為零,活動時為非零。 讀取輸出引腳的值時,返回的值應該是引腳上看到的值。 由於包括開漏訊號和輸出延遲在內的問題,這並不總是與指定的輸出值匹配。
get/set 呼叫不返回錯誤,因為應該更早地從 gpiod_direction_*() 報告“無效 GPIO”。 但是,請注意,並非所有平臺都可以讀取輸出引腳的值; 那些不能讀取的平臺應始終返回零。 此外,對於無法在沒有休眠的情況下安全訪問的 GPIO 使用這些呼叫(請參見下文)是一個錯誤。
可能休眠的 GPIO 訪問¶
必須使用基於訊息的匯流排(如 I2C 或 SPI)來訪問某些 GPIO 控制器。 讀取或寫入這些 GPIO 值的命令需要等待到達佇列的頭部以傳輸命令並獲取其響應。 這需要休眠,而這無法從 IRQ 處理程式內部完成。
支援此型別 GPIO 的平臺透過從此呼叫返回非零值來將其與其他 GPIO 區分開
int gpiod_cansleep(const struct gpio_desc *desc)
要訪問此類 GPIO,需要定義一組不同的訪問器
int gpiod_get_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
訪問此類 GPIO 需要可能休眠的上下文,例如執行緒化的 IRQ 處理程式,並且必須使用這些訪問器而不是沒有 cansleep() 名稱字尾的自旋鎖安全訪問器。
除了這些訪問器可能會休眠並且可以在無法從 hardIRQ 處理程式訪問的 GPIO 上工作之外,這些呼叫與自旋鎖安全呼叫的作用相同。
低電平有效和開漏語義¶
由於消費者不應關心物理線路電平,因此所有 gpiod_set_value_xxx() 或 gpiod_set_array_value_xxx() 函式都使用邏輯值進行操作。 透過這種方式,它們會考慮低電平有效屬性。 這意味著它們會檢查 GPIO 是否配置為低電平有效,如果是,則在驅動物理線路電平之前,它們會操作傳遞的值。
這同樣適用於開漏或開路源輸出線路:它們不會主動將其輸出驅動為高電平(開漏)或低電平(開路源),它們只是將其輸出切換為高阻抗值。 消費者不需要關心。(有關詳細資訊,請閱讀 GPIO 驅動程式介面 中的開漏。)
透過這種方式,所有 gpiod_set_(array)_value_xxx() 函式都將引數“value”解釋為“活動”(“1”)或“非活動”(“0”)。 物理線路電平將相應地驅動。
例如,如果設定了專用 GPIO 的低電平有效屬性,並且 gpiod_set_(array)_value_xxx() 傳遞了“活動”(“1”),則物理線路電平將被驅動為低電平。
總結一下
Function (example) line property physical line
gpiod_set_raw_value(desc, 0); don't care low
gpiod_set_raw_value(desc, 1); don't care high
gpiod_set_value(desc, 0); default (active high) low
gpiod_set_value(desc, 1); default (active high) high
gpiod_set_value(desc, 0); active low high
gpiod_set_value(desc, 1); active low low
gpiod_set_value(desc, 0); open drain low
gpiod_set_value(desc, 1); open drain high impedance
gpiod_set_value(desc, 0); open source high impedance
gpiod_set_value(desc, 1); open source high
可以使用 set_raw/get_raw 函式覆蓋這些語義,但應儘可能避免這樣做,特別是對於不應關心實際物理線路電平而應擔心邏輯值的與系統無關的驅動程式。
訪問原始 GPIO 值¶
存在一些消費者,他們需要管理 GPIO 線路的邏輯狀態,即他們的裝置實際接收的值,無論它與 GPIO 線路之間有什麼。
以下呼叫集忽略 GPIO 的低電平有效或開漏屬性,並處理原始線路值
int gpiod_get_raw_value(const struct gpio_desc *desc)
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
GPIO 的低電平有效狀態也可以使用以下呼叫來查詢和切換
int gpiod_is_active_low(const struct gpio_desc *desc)
void gpiod_toggle_active_low(struct gpio_desc *desc)
請注意,這些函式應謹慎使用; 驅動程式不應關心物理線路電平或開漏語義。
使用單個函式呼叫訪問多個 GPIO¶
以下函式獲取或設定 GPIO 陣列的值
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap)
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap)
int gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap)
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap)
該陣列可以是任意一組 GPIO。 如果相應的晶片驅動程式支援,這些函式將嘗試同時訪問屬於同一組或晶片的 GPIO。 在這種情況下,可以預期效能會得到顯著提高。 如果無法同時訪問,則將按順序訪問 GPIO。
這些函式採用四個引數
array_size - 陣列元素的數量
desc_array - GPIO 描述符陣列
array_info - 從
gpiod_get_array()獲取的可選資訊value_bitmap - 用於儲存 GPIO 值的點陣圖(get)或用於分配給 GPIO 的值的點陣圖(set)
可以使用 gpiod_get_array() 函式或其變體之一獲取描述符陣列。 如果該函式返回的描述符組與所需的 GPIO 組匹配,則可以透過簡單地使用 gpiod_get_array() 返回的 struct gpio_descs 來訪問這些 GPIO
struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
my_gpio_descs->info, my_gpio_value_bitmap);
也可以訪問完全任意的描述符陣列。 可以使用 gpiod_get() 和 gpiod_get_array() 的任意組合來獲取描述符。 之後,必須手動設定描述符陣列,然後才能將其傳遞給上述函式之一。 在這種情況下,應將 array_info 設定為 NULL。
請注意,為了獲得最佳效能,屬於同一晶片的 GPIO 應在描述符陣列中是連續的。
如果描述符的陣列索引與單個晶片的硬體引腳編號匹配,則可以實現更好的效能。 如果傳遞給 get/set 陣列函式的陣列與從 gpiod_get_array() 獲取的陣列匹配,並且還傳遞與該陣列關聯的 array_info,則該函式可能會採用快速點陣圖處理路徑,將 value_bitmap 引數直接傳遞給晶片的相應 .get/set_multiple() 回撥。 這允許利用 GPIO 組作為資料 I/O 埠,而不會損失太多效能。
gpiod_get_array_value() 及其變體的返回值成功時為 0,發生錯誤時為負值。 請注意與 gpiod_get_value() 的區別,後者在成功時返回 0 或 1 以傳達 GPIO 值。 使用陣列函式,GPIO 值儲存在 value_array 中,而不是作為返回值傳遞回來。
對映到 IRQ 的 GPIO¶
GPIO 線路通常可以用作 IRQ。 您可以使用以下呼叫獲取與給定 GPIO 對應的 IRQ 編號
int gpiod_to_irq(const struct gpio_desc *desc)
它將返回一個 IRQ 編號,如果無法完成對映,則返回一個負的 errno 程式碼(最可能的原因是該特定 GPIO 無法用作 IRQ)。 使用未設定為使用 gpiod_direction_input() 的輸入的 GPIO,或者使用最初不是來自 gpiod_to_irq() 的 IRQ 編號,是一個未檢查的錯誤。 不允許 gpiod_to_irq() 休眠。
從 gpiod_to_irq() 返回的非錯誤值可以傳遞給 request_irq() 或 free_irq()。 它們通常會由特定於板的初始化程式碼儲存到平臺裝置的 IRQ 資源中。 請注意,IRQ 觸發選項是 IRQ 介面的一部分,例如 IRQF_TRIGGER_FALLING,系統喚醒功能也是如此。
GPIO 和 ACPI¶
在 ACPI 系統上,GPIO 由裝置的 _CRS 配置物件列出的 GpioIo()/GpioInt() 資源描述。 這些資源不提供 GPIO 的連線 ID(名稱),因此有必要使用額外的機制來實現此目的。
符合 ACPI 5.1 或更高版本的系統可以提供一個 _DSD 配置物件,該物件除了其他事項外,可用於為 _CRS 中 GpioIo()/GpioInt() 資源描述的特定 GPIO 提供連線 ID。 如果是這種情況,它將由 GPIO 子系統自動處理。 但是,如果 _DSD 不存在,則需要在裝置驅動程式中提供 GpioIo()/GpioInt() 資源和 GPIO 連線 ID 之間的對映。
有關詳細資訊,請參閱 與 GPIO 相關的 _DSD 裝置屬性
與舊版 GPIO 子系統互動¶
許多核心子系統和驅動程式仍然使用基於整數的舊版介面來處理 GPIO。 強烈建議將這些更新為新的 gpiod 介面。 對於需要同時使用這兩個介面的情況,以下兩個函式允許將 GPIO 描述符轉換為 GPIO 整數名稱空間,反之亦然
int desc_to_gpio(const struct gpio_desc *desc)
struct gpio_desc *gpio_to_desc(unsigned gpio)
只要 GPIO 描述符 desc 未被釋放,desc_to_gpio() 返回的 GPIO 編號就可以安全地用作 gpio_*() 函式的引數。 同樣,傳遞給 gpio_to_desc() 的 GPIO 編號必須首先使用例如 gpio_request_one() 正確獲取,並且返回的 GPIO 描述符僅在直到使用 gpio_free() 釋放該 GPIO 編號時才被認為是有效的。
禁止使用一個 API 獲取的 GPIO 使用另一個 API 釋放,這是一個未經檢查的錯誤。