什么是GPIO?
GPIO----“通用目的輸入/輸出端口”----是一個靈活的軟件控制的數(shù)字信號。許多種類的芯片都會提供,嵌入式linux開發(fā)者和硬件定制者會對此比較熟悉。每個GPIO提供一位與特定的管腳(或是“球”,BGA(Ball Grid Array)封裝下)相連。單板電路圖會顯示外部硬件與GPIOs的連接關(guān)系。GPIO驅(qū)動可寫成通用的,便于單板setup代碼可以將這些管腳配置數(shù)據(jù)傳遞給驅(qū)動。
SOC處理器非常依賴于GPIO。某些情況下,普通管腳可以被配置為GPIO。大多數(shù)芯片至少擁有幾組類似的GPIO。可編程邏輯器件(如FPGAs)可以很容易提供GPIO。一些多功能芯片,如電源管理、聲音解碼等經(jīng)常具有一些這樣的管腳來彌補(bǔ)SOC芯片上面管腳的不足。同樣,也存在一些GPIO擴(kuò)展芯片,連接用于I2C或是SPI串行總線。多數(shù)PC南橋擁有幾組GPIO兼容的管腳(僅有BIOS固件知道如何使用它們)。
不同系統(tǒng)間的GPIO的確切作用不同。通用常有下面幾種:
----輸出值可寫(高=1,低=0)。一些芯片也可以選擇驅(qū)動這些值的方式,以便支持“線-或”或類似方案(開漏信號線)。
----輸入值可讀(1,0)。一些芯片支持輸出管腳回讀,這在線或的情況下非常有用(以支持雙向信號線)。GPIO控制器可能具有一個輸入防故障/防反跳邏輯,有時還會有軟件控制。
----輸入經(jīng)常被用作中斷信號,通常是邊沿觸發(fā),但也有可能是電平觸發(fā)。這些中斷可以配置為系統(tǒng)喚醒事件,從而將系統(tǒng)從低功耗模式喚醒。
----一個GPIO經(jīng)常被配置為輸入/輸出雙向,根據(jù)不同的產(chǎn)品單板需求,但也存在單向的情況。
----大多是GPIO可以在獲取到spinlock自旋鎖時訪問,但那些通過串行總線訪問的通常不能如此操作(休眠的原因)。一些系統(tǒng)中會同時存在這兩種形式的GPIO。
----在一個給定單板上,每個GPIO用于一個特定的目的,如監(jiān)控MMC/SD卡的插入/移除,檢查卡寫保護(hù)狀態(tài),驅(qū)動LED,配置發(fā)送器,串行總線位拆,觸發(fā)一個硬件看門狗,觸發(fā)一個開關(guān)之類的。
GPIO約定
注意,它被稱為一個約定,因?yàn)槟悴皇潜仨氁袷厮?。如果你不使用此種方式操作,也不會遭到任何懲罰。存在一些可移植性不是關(guān)鍵的應(yīng)用場景:GPIO經(jīng)常用作板級膠合邏輯,這些邏輯甚至在單板的不同版本之間都會改變,且不能用在那些連接不同的單板上。只有極少通用的標(biāo)準(zhǔn)功能可以是可移植的。其余的特性是平臺特有的,且對于膠合邏輯是關(guān)鍵(且危險)的。
另外,這不需要任何實(shí)現(xiàn)框架,只是一個接口。一個平臺可以將它實(shí)現(xiàn)為一個簡單的inline函數(shù)來存取芯片寄存器,另一個可能通過一個多個不同GPIO控制器的抽象委托實(shí)現(xiàn)。
?。ㄓ幸恍┛蛇x的代碼支持這種實(shí)施策略,這在本文后面會講到,但擔(dān)任客戶的GPIO接口驅(qū)動不要關(guān)心他是如何實(shí)現(xiàn)的。)
也就是說,如果平臺支持約定,驅(qū)動應(yīng)當(dāng)盡可能使用它。平臺必須在Kconfig中聲明GENERIC_GPIO來支持,并且提供一個《asm/gpio.h》文件。那些不能離開標(biāo)準(zhǔn)GPIO調(diào)用的驅(qū)動應(yīng)該具有一個依賴于GENERIC_GPIO的條目。要使GPIO調(diào)用有效,無論是“真實(shí)代碼”或是作為“optimized-away stubs”,驅(qū)動需要使用包含文件:
#include 《linux/gpio.h》
如果你堅持此約定,這樣別的開發(fā)者理解你的代碼會比較容易且可以幫助維護(hù)它。
注意:在那些需要的平臺上,這些操作包括I/0操作間隔(barriers);驅(qū)動不需要顯式添加它們。
標(biāo)識GPIO
GPIO使用一個無符號整型數(shù)進(jìn)行標(biāo)識,范圍0到MAX_INT。那些保留的“negative”(負(fù))數(shù)用于其他的目的,如標(biāo)識信號為“在此單板上無效”,或是指出錯誤。那些不涉及到基本硬件操作的代碼將這些整型數(shù)視為不透明的。
平臺定義了它們?nèi)绾问褂眠@些接口,并且通常為每個GPIO線使用#define宏定義符號,以便單板的啟動代碼與相關(guān)設(shè)計直接保持一致。與此相反,驅(qū)動應(yīng)該只使用從setup代碼傳遞給他們的GPIO號碼,使用platform_data來保存單板特定的管腳配置數(shù)據(jù)(與其它所需的單板特定數(shù)據(jù)一起)。這避免了移植問題。
例如:一個平臺給GPIOs使用號碼32-159,同時另一個使用0-63支持一個GPIO控制器集合,64-79支持另一個類型的GPIO控制器,且在一個特定的單板上80-95支持一個FPGA。號碼不必是連續(xù)的,這些單板也可以使用2000-2063來標(biāo)識一組用于I2CGPIO擴(kuò)展
如果你想要使用一個無效的GPIO號碼初始化一個結(jié)構(gòu)體,使用一些負(fù)數(shù)(可以為“-EINVAL”),它將永遠(yuǎn)不會有效。為了測試來自這樣一個結(jié)構(gòu)的這樣一個號碼是否能夠引用一個GPIO,你需要使用:
int gpio_is_valid(int number);
一個無效的號碼將被調(diào)用(可以是申請或釋放GPIO)拒絕。別的號碼也可能被拒絕。例如,一個號碼可能是有效的,但在給定的單板上臨時未使用。
平臺是否支持多個GPIO控制器是平臺特定實(shí)現(xiàn)的關(guān)鍵,同樣是否支持GPIO號碼空間的“空洞”,是否支持在運(yùn)行時增加新控制器也是關(guān)鍵。這些關(guān)鍵會影響多個事情,包括相鄰的GPIO號碼是否都有效。
?
使用GPIOs
要使用GPIO,系統(tǒng)首先要分配一個GPIO,使用gpio_request() 為系統(tǒng)分配一個GPIO。
接下來要做的一件事是標(biāo)示GPIO的方向,通常在使用GPIO建立一個platform_device時(位于單板的setup代碼中):
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
返回0標(biāo)示成功,或是一個負(fù)的errno錯誤碼。它應(yīng)該被檢查,因?yàn)間et/set調(diào)用沒有錯誤返回,且可能會有錯誤配置。你通常應(yīng)該在線程上下文中使用這些調(diào)用。雖然如此,對于spinlock-safe的GPIO,在tasking使能之前使用也是可以的,作為一個早期的單板建立。
對于輸出GPIO,value參數(shù)提供了初始輸出值。這有助于避免系統(tǒng)啟動過程中的信號干擾。
為了與GPIO早期的接口兼容,設(shè)置一個GPIO的方向,隱性要求申請GPIO。這個兼容性從可選的gpiolib架構(gòu)中移除了。
如果GPIO號碼無效或是指定的GPIO不能使用對應(yīng)模式操作的話,設(shè)置方向會失敗。依靠boot固件設(shè)置好GPIO的方向通常不是一個好主意,因?yàn)閎oot的功能可能沒有通過驗(yàn)證(除了boot linux)。(類似的,單板setup代碼可能需要將管腳復(fù)用為一個GPIO,和配置為合適的上拉/下拉。)
Spinlock-Safe GPIO訪問
-------------------------
大多數(shù)GPIO控制器可以使用內(nèi)存讀寫指令訪問。它們不需要休眠,且可以從內(nèi)部硬件中斷處理(非線程)和類似的上下文環(huán)境安全完成。
使用下列調(diào)用訪問這些GPIO,此時gpio_cansleep將總是返回錯誤
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
/* GPIO OUTPUT */
void gpio_set_value(unsigned gpio, int value);
其中,value是一個布爾型參數(shù),零表示低,非零表示高。當(dāng)讀一個輸出管腳的值時,返回的值應(yīng)該是在管腳上看到的值。..這并不總是與指定輸出值相匹配的,因?yàn)榇嬖陂_漏信號和輸出延遲問題。
get/set調(diào)用沒有錯誤返回,因?yàn)椤盁o效GPIO”應(yīng)該已經(jīng)由gpio_direction_*()提早報告了。雖然如此,并非所有的平臺都可以讀取輸出管腳的值,那些不能讀的應(yīng)該總是返回零。同時,對那些可能導(dǎo)致睡眠的GPIO使用這些接口是一個錯誤。
平臺的特定實(shí)現(xiàn)被鼓勵優(yōu)化這兩個調(diào)用以獲取GPIO值。在那些GPIO號碼是常量的情況下,它們通常只需一對指令(讀或?qū)懸粋€硬件寄存器)訪問,且不需要spinlock。這樣的優(yōu)化可以使位拆分應(yīng)用更有效率(在時間和空間上)(相比較于花費(fèi)一堆指令在子例程調(diào)用來說)。
可能睡眠的GPIO訪問
一些GPIO控制器必須使用基于消息的總線如I2C和SPI來進(jìn)行訪問。讀寫這些GPIO的命令需要等待到達(dá)發(fā)送隊(duì)列的開始和獲取它的響應(yīng)。這需要休眠,且不能從內(nèi)部中斷處理函數(shù)中完成。
對于這種GPIO調(diào)用gpio_cansleep接口將返回非零值(需要一個有效的GPIO號碼,并已經(jīng)提前使用gpio_request進(jìn)行分配)
int gpio_cansleep(unsigned gpio);
為了訪問這些GPIO,一個不同的訪問函數(shù)集被定義
/* GPIO INPUT: return zero or nonzero, might sleep */
int gpio_get_value_cansleep(unsigned gpio);
/* GPIO OUTPUT, might sleep */
void gpio_set_value_cansleep(unsigned gpio, int value);
訪問這樣的gpio需要一個可能睡眠的上下文,例如一個線程級別中斷處理程序,并且這些訪問函數(shù)必須代替那些沒有cansleep()后綴的spinlock-safe的函數(shù)。
除了這些訪問函數(shù)可能休眠,且對那些不能從硬件中斷處理函數(shù)中訪問的GPIO起作用外,這些調(diào)用與那些spinlock-safe的調(diào)用效果一致
** IN ADDITION ** calls to setup and configure such GPIOs must be made
from contexts which may sleep, since they may need to access the GPIO
controller chip too: (These setup calls are usually made from board
setup or driver probe/teardown code, so this is an easy constraint.)
附加的調(diào)用(用于建立和配置這樣的GPIO)必須出自可以休眠的上下文,因?yàn)樗鼈兛赡苄枰L問GPIO控制器芯片:(這些setup調(diào)用經(jīng)常出自單板setup或是驅(qū)動probe/teardown(拆卸)代碼,所以這是一個簡單(無關(guān)緊要)的限制。)
gpio_direction_input()
gpio_direction_output()
gpio_request()
## gpio_request_one()
##gpio_request_array()
## gpio_free_array()
gpio_free()
gpio_set_debounce()
主張和釋放GPIO
為了捕獲系統(tǒng)配置錯誤,定義了兩個調(diào)用
/* request GPIO, returning 0 or negative errno.
* non-null labels may be useful for diagnostics.
*/
int gpio_request(unsigned gpio, const char *label);
/* release previously-claimed GPIO */
void gpio_free(unsigned gpio);
錯誤的GPIO號會導(dǎo)致gpio_request()失敗,同樣申請一個已經(jīng)被主張的也會出錯。gpio_request()的返回值必須被檢查。通常應(yīng)該在一個任務(wù)上下文中調(diào)用此函數(shù),雖然如此,對于spinlock-safe GPIO來講,在使能tasking之前申請GPIO是可以的(作為早期單板setup的一部分)。
這些調(diào)用服務(wù)于兩個基本目的。一個是為了診斷目的標(biāo)識實(shí)際使用GPIO的信號,系統(tǒng)可能有幾百個GPIO,但在一個給定的單板上常常只有幾組處于使用狀態(tài)。另一個是為了捕獲沖突、標(biāo)示錯誤。當(dāng)兩個或多個驅(qū)動錯誤地認(rèn)為它們獨(dú)占此信號或是一些東西錯誤的認(rèn)為移除驅(qū)動是安全的,這時需要管理一個信號用于管理活動狀態(tài)。既是說,申請一個GPIO的過程可以為這種鎖服務(wù)。
一些平臺也使用GPIO用于功耗管理,如關(guān)掉不使用的芯片部分和更簡單的門控不使用的時鐘。
對于那些使用pinctrl子系統(tǒng)的管腳的GPIO,子系統(tǒng)應(yīng)該被通知它們的用途。一個gpiolib驅(qū)動的.request()操作可能調(diào)用pinctrl_request_gpio(),且一個gpiolib驅(qū)動的.free()操作可能調(diào)用pinctrl_free_gpio()。pinctrl子系統(tǒng)允許一個pinctrl_request_gpio()在一個管腳或是管腳組被一個設(shè)備擁有時成功,為了復(fù)用目的。
任意支持管腳復(fù)用硬件的編程需要路由GPIO信號到適當(dāng)?shù)墓苣_。這些應(yīng)該在一個GPIO驅(qū)動的.direction_input()或是.direction_output()操作中發(fā)生,且發(fā)生在任意一個輸出GPIO值setup之后。這允許從一個管腳的特殊功能到GPIO的無障礙集成。當(dāng)使用一個GPIO來實(shí)現(xiàn)一個關(guān)于一個非GPIO硬件模塊驅(qū)動一個典型信號的工作區(qū)(變通方案)時,這時常會被需求。
一些平臺允許一些或所有的GPIO信號被路由到不同的管腳。同樣的,GPIO或管腳的別的方面可能需要配置,如上來/下拉。平臺軟件應(yīng)該安排所有的這些細(xì)節(jié)優(yōu)先于gpio_request()之前配置。例如,使用pinctrl子系統(tǒng)的映射表,這樣GPIO使用者就不需要知道那些細(xì)節(jié)。
同樣注意:在你釋放GPIO之前需要停止使用它。
考慮到大多數(shù)場景中GPIO在它們被聲明后實(shí)際已經(jīng)被正確配置,定義了3各附加的調(diào)用:
/* request a single GPIO, with initial configuration specified by
* ‘flags’, identical to gpio_request() wrt other arguments and
* return value
*/
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
/* request multiple GPIOs in a single call
*/
int gpio_request_array(struct gpio *array, size_t num);
/* release multiple GPIOs in a single call
*/
void gpio_free_array(struct gpio *array, size_t num);
其中,flags參數(shù)當(dāng)前可以指定為下列屬性:
* GPIOF_DIR_IN- 配置方向?yàn)檩斎?/p>
* GPIOF_DIR_OUT- 配置方向?yàn)檩敵?/p>
* GPIOF_INIT_LOW- 作為輸出,設(shè)置初始值為低
* GPIOF_INIT_HIGH- 作為輸出,設(shè)置初始值為高
* GPIOF_OPEN_DRAIN- GPIO管腳是開漏極形式
* GPIOF_OPEN_SOURCE- GPIO管腳是開源極形式
由于GPIOF_INIT_*僅僅當(dāng)配置為輸出時有效,所以有效組合為
* GPIOF_IN- 配置為輸入
* GPIOF_OUT_INIT_LOW- 配置為輸出,初始為低電平
* GPIOF_OUT_INIT_HIGH- 配置為輸入,初始為高電平
當(dāng)設(shè)置flag為GPIOF_OPEN_DRAIN時,它將假設(shè)管腳是開漏極方式。這類管腳在輸出模式將不會被驅(qū)動為1。這樣的管腳需要連接上拉。通過使能此flag,當(dāng)它在輸出模式被要求設(shè)置為1時,gpio lib將使得方向?yàn)檩斎胍允沟霉苣_變高。輸出模式下,管腳輸出值0以驅(qū)動電平為低。
當(dāng)設(shè)置flag為GPIOF_OPEN_SOURCE,它假設(shè)管腳時開源極類型。這種管腳在輸出模式不能驅(qū)動為0。此種管腳需要下拉。通過使能這個flag,當(dāng)管腳要求輸出1時,gpio lib將使得方向變?yōu)檩斎胍允沟霉苣_變低。管腳在輸出模式驅(qū)動1為高
未來,這些flag可以被擴(kuò)展以支持更多的特性。
此外,為了簡化多個GPIO的聲明/釋放,引入了gpio結(jié)構(gòu)來壓縮這3個域
struct gpio {
unsignedgpio;
unsigned longflags;
const char*label;
};
一個典型用法的例子如下:
static struct gpio leds_gpios[] = {
{ 32, GPIOF_OUT_INIT_HIGH, “Power LED” }, /* default to ON */
{ 33, GPIOF_OUT_INIT_LOW, “Green LED” }, /* default to OFF */
{ 34, GPIOF_OUT_INIT_LOW, “Red LED” }, /* default to OFF */
{ 35, GPIOF_OUT_INIT_LOW, “Blue LED” }, /* default to OFF */
{ 。.. },
};
err = gpio_request_one(31, GPIOF_IN, “Reset Button”);
if (err)
。..
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
if (err)
。..
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));
GPIO映射到中斷
GPIO號碼是無符號整型數(shù),同樣中斷號碼也是這樣。這些構(gòu)成了兩個邏輯區(qū)別的名字空間(GPIO0無須對應(yīng)使用中斷0)。你可以在它們之間使用以下調(diào)用進(jìn)行映射:
/* map GPIO numbers to IRQ numbers */
int gpio_to_irq(unsigned gpio);
/* map IRQ numbers to GPIO numbers (avoid using this) */
int irq_to_gpio(unsigned irq);
它們返回一個對應(yīng)名字空間的對應(yīng)號碼,或者如果映射不能完成的話返回錯誤。(例如:一些GPIO不能用于中斷。)使用一個未setup的GPIO號碼作為輸出調(diào)用gpio_direction_input()或是使用一個不是來源于gpio_to_irq()的中斷號是一個未核對的錯誤,
這兩個映射調(diào)用會在單個增加或減少上有耗費(fèi)。它們不能休眠。
gpio_to_irq()的返回值(非錯誤)可以傳遞給request_irq()或free_irq()。它們經(jīng)常被保存到對應(yīng)platform設(shè)備的IRQ resource中,這使用單板特定的初始化函數(shù)完成。注意,中斷觸發(fā)選項(xiàng)是中斷接口的一部分,例如IRQF_TRIGGER_FALLING,作為系統(tǒng)喚醒能力。
irq_to_gpio()的返回值(非錯誤)通常用于gpio_get_value(),例如,為了在中斷被邊沿觸發(fā)時 初始化和更新驅(qū)動狀態(tài)。注意,一些platform不支持反轉(zhuǎn)映射,所以你應(yīng)該避免使用它。
仿真開漏信號
有時共享信號需要使用開漏信號,它只有低信號電平是實(shí)際驅(qū)動的。(此術(shù)語用于COMS晶體管,開集電極用于TTL。)一個上拉電阻引出高電平信號。這有時稱為“線與”,或是事實(shí)上更多的,來自負(fù)邏輯觀點(diǎn)(low=true)這是一個“線或”。
一個常見的開漏信號的例子是一個共享的低電平激活中斷線。同樣,雙向數(shù)據(jù)總線信號有時也使用開漏信號
一些GPIO控制器直接支持開漏輸出,更多的不支持。當(dāng)你需要開漏信號但你的硬件不直接支持時,一個常見的方法你可以使用任意的即可用于輸入也可以用于輸出的GPIO來模擬它
LOW:gpio_direction_output(gpio, 0) 。.. this drives the signal
and overrides the pullup.
HIGH:gpio_direction_input(gpio) 。.. this turns off the output,
so the pullup (or some other device) controls the signal.
如果你正在驅(qū)動信號為高,但是 gpio_get_value(gpio)報告了一個低值(經(jīng)過適當(dāng)?shù)纳仙龝r間準(zhǔn)備),你應(yīng)該知道可能是某些別的部件驅(qū)動了這個共享信號為低。這是不必要的錯誤。作為一個常見的例子,這是I2C時鐘拉伸的方式:一個從部件需要一個低速時鐘延遲了SCK的上升沿們,且I2C主設(shè)備因此調(diào)整了它的信號頻率。
這些約定節(jié)省了什么?
這些約定節(jié)省的一個大方面是關(guān)于管腳復(fù)用,因?yàn)檫@是高度芯片相關(guān)且不可移植的。一個平臺可能不需要顯式的管腳復(fù)用,另一個可能只有兩個選項(xiàng)用于任意給定的管腳,另一個可能每個管腳有八個選擇,一個可能能夠路由一個給定的GPIO到多個管腳中的一個。(是的,這些例子在今天的linux上都可以找到)
在一些平臺上配置和上拉/下拉的使能與多路復(fù)用相關(guān)。并不是所有的平臺都支持或是以同樣的方式支持它們,任意一個給定的單板可能使用外部上拉(或下拉)以便片上ons不能被使用。(當(dāng)一個電路需要5千歐姆,片上的100K歐姆電阻不能作用。)同樣的,驅(qū)動能力(2mA 對 20mA)和電壓(1.8V 對 3.3V)是平臺特定的,與模型一樣在配置的管腳和GPIO之間一一對應(yīng)
還有別的系統(tǒng)特定的機(jī)制此處并沒有提到,如上文提及的抗干擾和線或輸出。
硬件可能按組讀寫GPIO,但是那通常是單獨(dú)配置的:對于那些共享同一個bank的GPIO。
?。℅PIO通常16或32個為一組,一個給定的SOC系統(tǒng)一般擁有幾個這樣的BANK。)
一些系統(tǒng)可以從輸出GPIO管腳觸發(fā)中斷,或是從一個沒作為GPIO管理的管腳上讀值。依賴于這種機(jī)制的代碼將是不可移植的。
GPIO動態(tài)定義并不是當(dāng)前的標(biāo)準(zhǔn),例如,作為配置一個單板附加的GPIO擴(kuò)展器的邊界效應(yīng)。
GPIO系統(tǒng)結(jié)構(gòu)(可選)
如前面提醒的一樣,一個可選的實(shí)現(xiàn)結(jié)構(gòu)使得平臺支持不同種類的GPIO控制器使用同一個編程接口變得簡單。這個結(jié)構(gòu)稱為gpiolib。
作為一個調(diào)試目的,如果debugfs有效,一個/sys/kernel/debug/gpio文件在那里將被找到。它列出了所有的通過這個結(jié)構(gòu)注冊的GPIO控制器,和GPIO當(dāng)前的使用狀態(tài)。
Controller Drivers: gpio_chip
控制器驅(qū)動:gpio_chip
在這個架構(gòu)中,每個GPIO控制器被封裝為一個“gpio_chip”結(jié)構(gòu)體,此結(jié)構(gòu)體中包含了每個控制器的通用信息:
--確定GPIO方向的方法
--存取GPIO值的方法
--聲明方法是否休眠的flag
--可選的debugfs dump方法(展現(xiàn)附加的狀態(tài)如上拉配置等)
--用于診斷目的的標(biāo)簽
每個實(shí)例也有自己的私有數(shù)據(jù),可能來自device.platform_data:它的第一個GPIO和它暴露幾個GPIO.
實(shí)現(xiàn)一個gpio_chip的代碼應(yīng)該支持多個控制器的實(shí)例,可能使用驅(qū)動模型。代碼會配置每個gpio_chip并且執(zhí)行g(shù)piochip_add()。移除一個GPIO控制器是少見的,使用gpio_remove()移除一個不再有效的GPIO控制器。
大多數(shù)時候,一個gpio_chip是一個實(shí)例獨(dú)有的結(jié)構(gòu),它的一些狀態(tài)值不暴露給GPIO接口,如編址、電源管理等等。編碼解碼器之類的芯片會有復(fù)雜的非GPIO狀態(tài)。
所有的debugfs dump 方式通常應(yīng)該忽略那些未作為GPIO請求的信號。他們可以使用gpiochip_is_requested(),此函數(shù)返回與GPIO相關(guān)的label或是NULL。
平臺支持
為了支持這個結(jié)構(gòu),一個平臺的Kconfig需要選擇ARCH_REQUIRE_GPIOLIB或是ARCH_WANT_OPTIONAL_GPIOLIB之一,且安排的它的《asm/gpio.h》包含《asm-generic/gpio.h》并且定義3個函數(shù)gpio_get_value(), gpio_set_value(), 和 gpio_cansleep()。
他也可以提供一個自定義的值:ARCH_NR_GPIOS,以便能更好的反映平臺實(shí)際使用的GPIO數(shù)目,并不浪費(fèi)靜態(tài)區(qū)域空間。(它應(yīng)該計數(shù) 內(nèi)建/SOC GPIO和GPIO擴(kuò)展器擴(kuò)展的數(shù)目)
ARCH_REQUIRE_GPIOLIB意味著此平臺上gpiolib代碼將永久編譯進(jìn)內(nèi)核
ARCH_WANT_OPTIONAL_GPIOLIB意味著gpiolib代碼默認(rèn)是關(guān)閉的,用于可以使能它并且將它可選的編譯進(jìn)內(nèi)核。
如果這些選項(xiàng)都未被選上,平臺不能通過GPIO-lib支持GPIO,這些代碼也不能被用戶使能。
那些函數(shù)瑣細(xì)的實(shí)現(xiàn)可以直接使用架構(gòu)代碼,它們經(jīng)常通過gpio_chip分配:
#define gpio_get_value__gpio_get_value
#define gpio_set_value__gpio_set_value
#define gpio_cansleep__gpio_cansleep
愛好者實(shí)現(xiàn)可以代替定義他們使用內(nèi)聯(lián)函數(shù),使用邏輯優(yōu)化存取特定的基于SOC的GPIO。例如,如果 引用的GPIO是常數(shù)“12”,getting或setting它的值可能只需要2個或3個指令,且從不休眠。如果這樣一個優(yōu)化是不可能的話,這些調(diào)用實(shí)現(xiàn)必須委托給架構(gòu)代碼,它會耗費(fèi)至少幾十個指令。為了位拆型I/O,這些指令的節(jié)約是有相當(dāng)大的意義的。
對于SOC來說,平臺特定的代碼為每個bank的片上GPIO定義和注冊了gpio_chip實(shí)例。那些GPIO應(yīng)該被編號和打上標(biāo)簽以匹配芯片廠商文檔,且直接匹配單板設(shè)計圖。他們可以從零開始一直到平臺特定的限制。這些GPIO通常集成到單板初始化過程中以使得它們總是有效的,從arch_initcall()到更早,它們總是可以為中斷服務(wù)。
板級支持
對于外部GPIO控制器(如I2C或SPI擴(kuò)展)、ASIC、多功能器件、FPGA或是CPLD,通常單板私有代碼例程注冊控制器器件且確定它們的驅(qū)動使用什么GPIO號來調(diào)用gpiochip_add。它們的號碼經(jīng)常在平臺特定GPIO之后開始。
例如,單板setup代碼可以創(chuàng)建結(jié)構(gòu)標(biāo)示芯片想要暴露的GPIO的范圍,且使用platform_data傳遞它們到每個GPIO擴(kuò)展器芯片。這樣芯片驅(qū)動的probe()歷程可以傳遞這些數(shù)據(jù)到gpiochip_add()。
初始化順序是很重要的。例如當(dāng)一個依賴于基于I2C的GPIO的設(shè)備,它的probe()例程應(yīng)該僅能在GPIO有效后調(diào)用。這意味著設(shè)備不能在GPIO可以工作之前注冊。一個解決這樣依賴的方法是在板級特定代碼中,對于這種gpio_chip控制器來提供setup()和teardown()回調(diào),這些板級特定的回調(diào)將注冊設(shè)備一旦所有的需要資源有效時,并且在GPIO控制器無效時將它們移除。
用戶空間的Sysfs接口(可選)
使用gpiolib實(shí)現(xiàn)結(jié)構(gòu)的平臺可以選擇為GPIO配置一個sysfs用戶接口。這與debugfs接口不同,因?yàn)樗峁┝烁采wGPIO方向和值的控制而不只是顯示一個gpio狀態(tài)信息摘要。另外,它可以在產(chǎn)品系統(tǒng)中提供而不需要調(diào)試支持。
為系統(tǒng)給出對應(yīng)的硬件文檔,用戶空間可以知道例如GPIO#23控制著保護(hù)線,用于保護(hù)flash中的boot區(qū)域。系統(tǒng)升級程序可能需要臨時移除這個保護(hù),首先引入一個GPIO,然后改變它的輸出狀態(tài),接下來在重新使能寫保護(hù)之前升級代碼。通常用法中,GPIO#23將不會被觸碰,并且內(nèi)核也不需知道它的信息。
同樣依靠一個合適的硬件文檔,在一些系統(tǒng)用戶空間,GPIO可以被用于決定那些內(nèi)核并不關(guān)心的系統(tǒng)配置數(shù)據(jù)。對于一些任務(wù),簡單用戶空間GPIO驅(qū)動是系統(tǒng)真正需要的
注意,針對通用“LED和按鈕”的標(biāo)準(zhǔn)內(nèi)核驅(qū)動存在對應(yīng)的GPIO任務(wù)“l(fā)eds-gpio”和“gpio-keys”。使用它們代替直接與GPIO通話,它們集成在內(nèi)核架構(gòu)比你的用戶態(tài)代碼可能更好。
Paths in Sysfs
Sysfs路徑
There are three kinds of entry in /sys/class/gpio:
/sys/class/gpio有3個入口條目:
-控制接口用于用戶空間獲取GPIO控制
-GPIO自己
-GPIO控制器(“gpio_chip”實(shí)例)
這是對于標(biāo)準(zhǔn)文件的補(bǔ)充,包括“device”符號
控制接口是只寫的:
/sys/class/gpio/
“export” ————通過寫GPIO的號碼到此文件,用戶空間可以要求內(nèi)核導(dǎo)出一個GPIO的控制到用戶空間
例如:“echo 19 》 export”將創(chuàng)建一個GPIO #19的“gpio19”節(jié)點(diǎn)(假設(shè)內(nèi)核代碼未申請此GPIO號)。
“unexport”————與“export”效果相反
例如:“echo 19 》 unexport”將移除一個由“export”文件導(dǎo)出的“gpio19”節(jié)點(diǎn)。
GPIO信號擁有如/sys/class/gpio/gpio42/(對應(yīng)于GPIO#42)的路徑,并且具有下列讀寫屬性:
/sys/class/gpio/gpioN/
“direction”————讀為“in”或是“out”。這個值通??蓪?。寫“out”默認(rèn)初始化此值為低。為了確定無障礙操作,值“l(fā)ow”和“high”可以被寫入以配置GPIO的輸出初始化值。
注意這個屬性“將不存在”如果內(nèi)核不支持改變一個GPIO的方向,或者它不能被內(nèi)核代碼導(dǎo)出(不能顯式的允許用戶空間來重新配置GPIO的選項(xiàng)。)
“value”—————讀作“0”(低)或“1”(高)。如果GPIO被配置為一個輸出,這個值可寫;任何非零值均被視為高。
如果管腳可以被配置為中斷產(chǎn)生中斷管腳,且如果它已經(jīng)被配置為產(chǎn)生中斷(參考“edge”描述),你可以poll(2)此文件并且當(dāng)中斷觸發(fā)時poll(2)將返回。如果你使用了poll(2),設(shè)置POLLPRI和POLLERR事件。如果你使用select(2),在exceptfds中設(shè)置文件描述符。在poll(2)返回之后,有兩個選擇一是lseek(2)到sysfs文件的開始且讀新的值,另一個是關(guān)閉文件且重打開它來讀取新的值。(為何這樣設(shè)置?)
“edge”————讀作“none”、“rising”、“falling”或是“both”。寫這些字符串以選擇邊沿信號,他將使得“value”文件上的poll(2)操作返回。
這個文件只在管腳可以配置為中斷產(chǎn)生輸入管腳時存在。
“active_low”————讀為0(false)或1(true)。寫任何非零值都會反轉(zhuǎn)讀或?qū)懙闹?。目前和后來的poll(2)支持經(jīng)由edge屬性配置為“rising”或“falling”上升沿或下降沿將遵循這個設(shè)置。
GPIO控制器具有如/sys/class/gpio/gpiochip42/(針對控制器,實(shí)現(xiàn)GPIO開始于#42)的路徑,且具有下列制度屬性:
/sys/class/gpio/gpiochipN/
“base”————與N相等,是第一個被此芯片管理的GPIO
“l(fā)abel”————提供用于診斷(并不總是獨(dú)一無二的)
“ngpio”————管理的GPIO數(shù)(N到N+ngpio-1)
大多數(shù)情況下,單板文檔應(yīng)該提供GPIO的使用目的。雖然如此這些號碼并非總是固定的,一個子板上的GPIO可能與基礎(chǔ)板使用的不同。此種情況下,你可能需要使用gpiochip節(jié)點(diǎn)(可能與設(shè)計結(jié)合)來為每個信號決定正確的GPIO號碼。
從內(nèi)核代碼中導(dǎo)出
內(nèi)核代碼可以顯式管理那些使用gpio_request()申請的GPIO的導(dǎo)出
/* export the GPIO to userspace */
int gpio_export(unsigned gpio, bool direction_may_change);
/* reverse gpio_export() */
void gpio_unexport();
/* create a sysfs link to an exported GPIO node */
int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
/* change the polarity of a GPIO node in sysfs */
int gpio_sysfs_set_active_low(unsigned gpio, int value);
一個內(nèi)核驅(qū)動申請一個GPIO后,它可以使用gpio_export()使得sysfs接口有效。驅(qū)動可以控制信號方向是否可以改變。這使得驅(qū)動可以防止用戶空間代碼不小心沖擊重要的系統(tǒng)狀態(tài)。
明確的exporting有助于調(diào)試(使得一些實(shí)驗(yàn)更簡單),或是提供一個總是可以使用的接口,適合于bsp文檔。
GPIO被導(dǎo)出后,gpio_export_link()允許在sysfs的任何地方創(chuàng)建GPIO sysfs節(jié)點(diǎn)的符號鏈接。驅(qū)動可以用此在它們自己設(shè)備sysfs目錄下提供指定名字的接口(鏈接到GPIO節(jié)點(diǎn))
驅(qū)動可以使用gpio_sysfs_set_active_low()隱藏GPIO在用戶空間和單板之間的線極性不同。這僅影響sysfs接口。極性變換可以在gpio_export()之前和之后完成,并且前面使能的poll(2) (支持上升沿或下降沿事件)將被重新配置為遵循此設(shè)置。
評論