LinuxGPIO中文文檔


本文來自Linux官方文檔英文版,由於需要使用Linux的GPIO進行實驗,我翻譯了這篇文檔。

本文檔描述了GPIO框架的使用者接口。注意它描述了新的基於描述符的接口。
不推薦使用的基於整數的GPIO接口請參考gpio-legacy.txt。

獲取和使用GPIO的函數可以通過include以下文件來獲得:

#include <linux/gpio/consumer.h>

基於描述符的GPIO接口的所有函數都是以gpiod_為前綴。 gpio_前綴用於傳統接口。內核中的其他函數不應該使用這些前綴。強烈建議不要使用legacy的接口函數,新的代碼應該使用<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參數的更詳細說明請參閱Documentation/gpio/board.txt

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相同,但強制以開漏的方式使用
    \end{itemize}

最后兩個標志用於必須開漏方式的情況,比如GPIO被用作I2C時,如果該GPIO尚未在映射(參見board.txt)中被配置為開漏方式,將被強制配置為開漏方式並給出WARNING。

這兩個函數都返回有效的GPIO描述符或可被IS_ERR()檢查的錯誤代碼(它們永遠不會返回NULL指針)。 返回-ENOENT只會發生在當且僅當沒有為設備/功能/索引三元組成功分配GPIO的時候。其他錯誤代碼用於已成功分配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,其中包含一個描述符數組:

struct gpio_descs {
		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 * ()提供了nodirection設置標志,則可以通過調用gpiod_direction _*()函數之一來完成:

int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)

成功返回值為零,否則返回值為負的錯誤代碼。該返回值應該被檢查,因為get/set調用不會返回錯誤,所以錯誤的配置是有可能的。您通常應該在任務上下文進行這些調用。但是,對於自旋鎖安全(Spinlock-Safe)的GPIO,可以作為板級設置初期的一部分,在啟用任務之前使用它們。

對於輸出GPIO,提供的值將成為初始輸出值。這有助於避免系統啟動期間的信號故障。驅動程序還可以查詢GPIO的當前方向:

int gpiod_get_direction(const struct gpio_desc *desc)

此函數返回0表示輸出,1表示輸入,或錯誤代碼(如果出錯)

要意識到GPIO沒有默認的方向。因此,\emph{使用GPIO而不首先設置其方向是非法的,並且將導致未定義的行為!}

Spinlock-Safe的GPIO訪問

大多數GPIO控制器可通過存儲器讀/寫指令訪問。對於不能睡眠,並且可以安全地從內部hard(非線程的)IRQ handler和類似的上下文中完成的操作(即原子操作中),使用以下調用來訪問GPIO:

int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);

value為布爾值,零為低,非零為高。讀取輸出引腳的值時,返回的值應該是引腳上的值。由於包括開漏信號和輸出延遲在內的問題,它並不總是匹配指定的輸出值。

get/set調用不會返回錯誤,因為“無效的GPIO”應該在這之前就從gpiod_direction_*()中得知。但請注意,並非所有平台都可以讀取輸出引腳的值;對於那些不能讀取的平台,函數永遠返回零。另外,使用這些函數訪問需要睡眠才能安全訪問的GPIO(見下文)是錯誤的操作。

允許睡眠的GPIO訪問

有些GPIO控制器必須使用基於消息的總線(如I2C或SPI)訪問。讀取或寫入這些GPIO值的命令需要等待到達隊列的頭部以傳輸命令並獲得其響應。

這樣就需要允許睡眠,導致這類GPIO的訪問不能在內部IRQ處理程序內(原子上下文)完成。

支持這種類型的GPIO的平台通過從以下調用返回非零來區別於其他GPIO:

int gpiod_cansleep(const struct gpio_desc *desc)

要訪問此類GPIO,請使用另一組GPIO訪問函數:

int gpiod_get_value_cansleep(const struct gpio_desc * desc)
void gpiod_set_value_cansleep(struct gpio_desc * desc,int value)

訪問這樣的GPIO需要一個可以休眠的上下文,例如一個threaded IRQ處理程序,並且必須使用上述訪問函數而不是Spinlock-Safe的沒有cansleep()后綴的訪問函數。

除了可以睡眠,無法在hardIRQ處理程序訪問的特點以外,這些調用與Spinlock-Safe的調用相同。

低有效和開漏語義

由於使用者不必關心物理線路級別,所有的gpiod_set_value_xxx()或 gpiod_set_array_value_xxx() 函數都以邏輯值操作。通過這種方式,它們會將低電平有效的性質考慮在內。這意味着它們會檢查GPIO是否配置為低電平有效,如果是,它們會在物理線路電平被驅動之前調整傳遞的值。

這同樣適用於開漏或開源輸出:它們並不輸出高電平(開漏)或低電平(開源),它們只是將輸出切換到高阻抗值。使用者應該不需要關注。(有關的詳細信息,請參閱driver.txt中關於開漏的細節。)

這樣,所有gpiod_set_(array)_value_xxx()函數都將參數“value”解釋為“asserted”(“1”)或“de-asserted”(“0” )。相應地驅動物理線路級別。

例如,如果設置了GPIO的低電平有效屬性,並且gpiod_set_(array)_value_xxx()傳遞了“asserted”(“1”),則物理線路電平將被驅動為低電平。

總結:

函數(示例) 線路屬性 物理線路
gpiod_set_raw_value(desc, 0); - 低電平
gpiod_set_raw_value(desc, 0); - 高電平
gpiod_set_value(desc, 0); 默認(高電平有效) 低電平
gpiod_set_value(desc, 1); 默認(高電平有效) 高電平
gpiod_set_value(desc, 0); 低電平有效 高電平
gpiod_set_value(desc, 1); 低電平有效 低電平
gpiod_set_value(desc, 0); 開漏 低電平
gpiod_set_value(desc, 1); 開漏 高阻態
gpiod_set_value(desc, 0); 開漏 高阻態
gpiod_set_value(desc, 1); 開漏 高電平

可以使用*set_raw /'get_raw函數覆蓋這些語義,但應盡可能避免,尤其是系統無關的驅動程序,它們不需要關心實際的物理線路級別而是關心邏輯值。

訪問原始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)

請注意,這些函數只能在使用者明白自己在做什么的情況下使用;驅動程序一般不應該關心線路物理狀態或開漏語義。

使用單個函數調用訪問多個GPIO

以下函數獲取或設置GPIO數組的值:

int gpiod_get_array_value(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array);
int gpiod_get_raw_array_value(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array);
int gpiod_get_array_value_cansleep(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array);

void gpiod_set_array_value(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array)
void gpiod_set_raw_array_value(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array)
void gpiod_set_array_value_cansleep(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
		struct gpio_desc **desc_array,
		int *value_array)

數組可以是任意一組GPIO。如果相應的芯片驅動器支持,這些函數將嘗試同時訪問屬於同一存儲體或芯片的GPIO。在這種情況下,可以預期顯著改善的性能。如果無法同時訪問,GPIO將按順序訪問。

這些函數有三個參數:

  • array_size - 數組元素的數量
  • desc_array - GPIO描述符數組
  • value_array - 存儲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_values);

也可以訪問完全任意的描述符數組。可以使用gpiod_get()和gpiod_get_array()的任意組合來獲得描述符。之后,必須先手動設置描述符數組才能將其傳遞給上述函數之一。

注意,為了獲得最佳性能,屬於同一芯片的GPIO應該在描述符數組中是連續的。

gpiod_get_array_value()及其變體成功時返回0,錯誤返回負數。請注意與gpiod_get_value()的區別,gpiod_get_value()在成功傳遞GPIO值時返回0或1。使用數組函數時,GPIO值存儲在value_array中,而不是作為返回值傳回。

GPIO映射到IRQ

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()資源與GPIOconnection ID之間的映射需要由設備驅動程序提供。

有關詳細信息,請參閱Documentation/acpi /gpio-properties.txt

使用舊版GPIO子系統進行交互

許多內核子系統仍使用傳統的基於整數的GPIO接口。雖然強烈建議將它們升級到更安全的基於描述符的API,但以下兩個函數允許您將GPIO描述符轉換為GPIO整數命名空間,及其反向轉換:

int desc_to_gpio(const struct gpio_desc *desc)
struct gpio_desc *gpio_to_desc(unsigned gpio)

只要沒有釋放GPIO描述符,就可以安全地使用desc_to_gpio()返回的GPIO號。同樣,必須正確獲取傳遞給gpio_to_desc()的GPIO號,並且只有在獲取GPIO號后才能使用返回的GPIO描述符。

禁止使用不同類的API分別獲取和釋放GPIO,這是一個未進行檢查的錯誤。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM