一、GPIO重要概念
要想操作GPIO引腳,需要先把所用引腳配置成GPIO功能,這個通過pinctrl子系統來實現。然后可以根據設置的引腳的方向來讀取引腳的值和設置輸出值。GPIO子系統存在之前,我們驅動需要在代碼中配置寄存器來使用GPIO引腳。再BSP工程師實現好GPIO子系統后,我們就可以在設備樹中指定GPIO引腳,在驅動中使用GPIO子系統的標准函數來獲取GPIO、設置GPIO方向、讀取/設置GPIO的值。這樣的驅動代碼是於單板無關的。
二、GPIO內核相關文檔
Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
Documentation\gpio\gpio.txt
Documentation\devicetree\bindings\gpio\gpio.txt
三、GPIO設備樹配置
1. BSP工程師實現的gpio驅動,驅動工程師直接在設備樹中配置使用。
//client節點 device { led-gpios = <組, 哪個幾個,flag>; //"組"是必須要有的元素,為gpio控制器的描述,這里除了組之外還有幾個域是由組中的#gpio-cells的值決定的。 }; //service端,設備樹中對一個gpio控制器的表示: gpio1 { ...... gpio-controller; #gpio-cells = <2>; //表示client使用gpio1這一組中的某個引腳時,除了組之外還需要使用2個整數來表示。 };
2. 舉個例子:
foo_device { compatible = "acme,foo"; ...... led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH> /*red*/ //一般這里可能為&gpioX <&gpio 16 GPIO_ACTIVE_HIGH> /*green*/ <&gpio 17 GPIO_ACTIVE_HIGH>; /*blue*/ power-gpios = <&gpio, 1 GPIO_ACTIVE_LOW>; //注意這里使用了avtive_low屬性了 };
驅動代碼中:
gpiod_get_index(dev, "led", 0, GOIOD_OUT_HIGH); //取出設備樹中名為led的gpio中的第0個引腳,也就是red。 gpiod_get_index(dev, "led", 1, GOIOD_OUT_HIGH); //取出第1個
若在設備樹中只定義了一個引腳,就可以使用:
gpiod_get(dev, "power", GPIO_OUTPUT_HIGH); //把這個設備下名為power的那個引腳給取出來。
三、在驅動中使用GPIO
1.GPIO子系統有兩套接口
(1) 一是基於描述符(descriptor-based)的,相關api函數都是以"gpiod_"為前綴,它使用gpio_desc結構來表示一個引腳。
(2) 另一種是老(legency)的,相關api函數都是以"gpio_"為前綴,它使用一個整數來表示一個引腳。
要操作一個引腳,首先要get引腳,然后設置方向,然后讀取、寫值。
2.列舉操作GPIO常使用的函數
//1.獲取GPIO gpiod_get //legency為gpio_request gpiod_get_index gpiod_get_array //legency為gpio_request_array devm_gpiod_get devm_gpiod_get_index devm_gpiod_get_array //2.設置方向 gpiod_direction_input //legency為gpio_direction_input gpiod_direction_output //legency為gpio_direction_input //3.讀值、寫值 gpiod_get_value //legency為gpio_get_value gpiod_set_value //legency為gpio_set_value //4.釋放GPIO gpio_free //gpio_free gpiod_put //gpio_free_array gpiod_put_array devm_gpiod_put devm_gpiod_put_array
前綴為"devm_"的含義是設備資源管理,這是一種自動釋放資源的機制。它的思想是“資源是屬於設備的,設備不存在時資源就可以自動釋放”。在Linux驅動開發過程中,先申請了GPIO,再申請內存,如果內存申請失敗,那么在返回之前就需要先釋放GPIO資源。如果使用的是devm相關函數,在內存申請失敗時可以直接返回,設備的銷毀函數會自動地釋放已經申請了的GPIO資源。建議使用devm相關函數操作GPIO。
3.如何通過GPIO號來使用GPIO
比如要通過gpio號來操作原理圖上的GPIO5_14這個gpio引腳,那么得先知道這個gpio引腳的number是多少。那么得先找到gpio5組的基gpio number是多少。
/sys/class/gpio/# ls export gpio30 gpiochip0 gpiochip32 gpiochip64 gpiochip96 gpiochip128 gpiochip504 unexport //gpiochip96 這一組gpio的基gpio號是96 /sys/class/gpio/gpiochip128# ls base device label ngpio power subsystem uevent /sys/class/gpio/gpiochip128# cat label //可以看出設備樹節點名為gpio@20ac000,20ac000就是這一組gpio寄存器的基地址,可通過它查看每一組gpio的基gpio號 20ac000.gpio /sys/class/gpio/gpiochip128# cat base //表示這一組gpio的基gpio號為128 128 /sys/class/gpio/gpiochip128# cat ngpio //表示這一組gpio的個數 32
在dtsi文件中檢索20ac000就可以看到如下設備樹配置,所以知道gpio5這一組gpio的基gpio號是128
gpio5: gpio@20ac000 { compatible = "fsl,im6ul-gpio", "fsl,imx35-gpio"; reg = <0x020ac000 0x4000>; interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>; gpio-controller; #gpio-cells = <0x2>; interrupt-controller; #interrupt-cells = <0x2>; };
那么GPIO5_14,其gpio號就是128+14=142,然后可以將這個gpio給導出來,設置其方向,配置其值,進行驗證。
/sys/class/gpio# echo 142 > export /sys/class/gpio# ls export unexport gpio142 ... //此時可以看到多了一個gpio142目錄 /sys/class/gpio/gpio142# ls active_low direction power uevent device edge subsystem value //此時可以cat active_low 看是否具有active_low屬性 /sys/class/gpio/gpio142# echo in > direction //設置為輸入引腳 /sys/class/gpio/gpio142# cat value //讀取其值 1 /sys/class/gpio# echo 142 > unexport //取消映射
如果某個引腳已經被使用了,再次export就會報錯:“resource busy”。算出引腳號后就可以在驅動中使用legency函數來通過gpio號來操作gpio引腳了。
注意: Qcom平台,如果內核級驅動程序通過of_get_named_gpio()函數或類似函數獲取,請求並使用該GPIO,則無法將該GPIO導出以進行sysfs控制。
五、active_low屬性
注意,設置的邏輯電平並不一定等於物理電平,因為有active_low屬性,若在獲取GPIO的時候指定了active_low屬性,那么設置為1就是低電平,設置為0才是高電平。但是也有一些函數直接忽略active_low屬性,整理如下:
unction(example) active-low屬性 物理電平 gpiod_set_raw_value(desc, 0) don't care 0 gpiod_set_raw_value(desc, 1) don't care 1 gpiod_set_value(desc, 0) 是 1 gpiod_set_value(desc, 0) 否 0 gpiod_set_value(desc, 1) 是 0 gpiod_set_value(desc, 1) 否 1