gpio子系統簡介
如果pinctrl子系統將一個PIN複用爲GPIO的話,就要用到GPIO子系統。gpio 子系統顧名思義,就是用於初始化 GPIO 並且提供相應的 API 函數,比如設置 GPIO爲輸入輸出,讀取 GPIO 的值等。 gpio 子系統的主要目的就是方便驅動開發者使用 gpio,驅動開發者在設備樹中添加 gpio 相關信息,然後就可以在驅動程序中使用 gpio 子系統提供的 API函數來操作 GPIO, Linux 內核向驅動開發者屏蔽掉了 GPIO 的設置過程,極大的方便了驅動開發者使用 GPIO
I.MX6ULL的gpio子系統驅動
設備樹中的 gpio 信息
I.MX6ULL-ALPHA 開發板上的 UART1_RTS_B 做爲 SD 卡的檢測引腳, UART1_RTS_B 複用爲 GPIO1_IO19,通過讀取這個 GPIO 的高低電平就可以知道 SD 卡有沒有插入。首先肯定是將 UART1_RTS_B 這個 PIN 複用爲 GPIO1_IO19,並且設置電氣屬性。打開dts文件,UART1_RTS_B 這個 PIN 的 pincrtl 設置如下:
== SD 卡 CD 引腳 PIN 配置參數==
316 pinctrl_hog_1: hoggrp-1 {
317 fsl,pins = <
318 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
......
322 >;
323 };
第 318 行,設置 UART1_RTS_B 這個 PIN 爲 GPIO1_IO19。
pinctrl 配置好以後就是設置 gpio 了, SD 卡驅動程序通過讀取 GPIO1_IO19 的值來判斷 SD卡有沒有插入,但是 SD 卡驅動程序怎麼知道 CD 引腳連接的 GPIO1_IO19 呢?肯定是需要設備樹告訴驅動啊!在設備樹中 SD 卡節點下添加一個屬性來描述 SD 卡的 CD 引腳就行了, SD卡驅動直接讀取這個屬性值就知道 SD 卡的 CD 引腳使用的是哪個 GPIO 了。 SD 卡連接在I.MX6ULL 的 usdhc1 接口上,在 .dts文件中 中找到名爲“usdhc1”的節點,這個節點就是 SD 卡設備節點,如下所示:
&usdhc1 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc1>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
keep-power-in-suspend;
enable-sdio-wakeup;
vmmc-supply = <®_sd1_vmmc>;
status = "okay";
};
從上面看到沒有指定CD引腳的pinctrl信息,那麼SD卡驅動就沒辦法設置CD引腳定的複用功能?這個不用擔心,因爲在“iomuxc”節點下引用了pinctrl_hog_1這個節點,所以linux內核中的iomuxc驅動就會自動初始化pinctrl_hog_1節點下的所有PIN
屬性“cd-gpios”描述了 SD 卡的 CD 引腳使用的哪個 IO。屬性值一共有三個,我們來看一下這三個屬性值的含義,“&gpio1”表示 CD 引腳所使用的 IO 屬於 GPIO1 組,“19”表示 GPIO1 組的第 19 號 IO,通過這兩個值 SD 卡驅動程序就知道 CD 引腳使用了 GPIO1_IO19這 GPIO。“GPIO_ACTIVE_LOW”表示低電平有效,如果改爲“GPIO_ACTIVE_HIGH”就表示高電平有效。
根據上面信息,SD卡驅動程序就可以使用GPIO1_IO19來檢測SD卡的CD信號了
gpio1節點
504 gpio1: gpio@0209c000 {
505 compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
506 reg = <0x0209c000 0x4000>;
507 interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
508 <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
509 gpio-controller;
510 #gpio-cells = <2>;
511 interrupt-controller;
512 #interrupt-cells = <2>;
513 };
505行: 設置 gpio1 節點的 compatible 屬性有兩個,分別爲“fsl,imx6ul-gpio”和“fsl,imx35-gpio”,在 Linux 內核中搜索這兩個字符串就可以找到 I.MX6UL 的 GPIO 驅動程序。
506 行,的 reg 屬性設置了 GPIO1 控制器的寄存器基地址爲 0X0209C000
509 行,“gpio-controller”表示 gpio1 節點是個 GPIO 控制器。
510 行,“#gpio-cells”屬性和“#address-cells”類似, #gpio-cells 應該爲 2,表示一共有兩個 cell,第一個 cell 爲 GPIO 編號,比如“&gpio1 3”就表示 GPIO1_IO03。第二個 cell 表示GPIO 極 性 , 如 果 爲 0(GPIO_ACTIVE_HIGH) 的 話 表 示 高 電 平 有 效 , 如 果 爲1(GPIO_ACTIVE_LOW)的話表示低電平有效。
gpio子系統API函數
設置好設備樹以後就可以使用 gpio 子系統提供的 API 函數來操作指定的 GPIO, gpio 子系統向驅動開發人員屏蔽了具體的讀寫寄存器過程。這就是驅動分層與分離的好處,大家各司其職,做好自己的本職工作即可。 gpio 子系統提供的常用的 API 函數有下面幾個:
- gpio_request函數
- gpio_free函數
- gpio_direction_input函數
- gpio_directionn_output函數
- gpio_get_value函數
- gpio_set_value 函數
1.gpio_request函數
gpio_request 函數用於申請一個 GPIO 管腳,在使用一個 GPIO 之前一定要使用 gpio_request進行申請,函數原型如下:
int gpio_request(unsigned gpio, const char *label)
函數參數和返回值含義如下:
gpio: 要申請的 gpio 標號,使用 of_get_named_gpio 函數從設備樹獲取指定 GPIO 屬性信息,此函數會返回這個 GPIO 的標號。
label: 給 gpio 設置個名字。
返回值: 0,申請成功;其他值,申請失敗。
2.gpio_free 函數
如果不使用某個 GPIO 了,那麼就可以調用 gpio_free 函數進行釋放。函數原型如下:
void gpio_free(unsigned gpio)
函數參數和返回值含義如下:
gpio: 要釋放的 gpio 標號。
返回值: 無
3.gpio_direction_input 函數
此函數用於設置某個 GPIO 爲輸入,函數原型如下所示:
int gpio_direction_input(unsigned gpio)
函數參數和返回值含義如下:
gpio:要設置爲輸入的 GPIO 標號。
返回值: 0,設置成功;負值,設置失敗
4.gpio_direction_output 函數
此函數用於設置某個 GPIO 爲輸出,並且設置默認輸出值,函數原型如下:
int gpio_direction_output(unsigned gpio, int value)
函數參數和返回值含義如下:
gpio:要設置爲輸出的 GPIO 標號。
value: GPIO 默認輸出值。
返回值: 0,設置成功;負值,設置失敗。
5.gpio_get_value 函數
此函數用於獲取某個 GPIO 的值(0 或 1),此函數是個宏,定義所示:
#define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio)
函數參數和返回值含義如下:
gpio:要獲取的 GPIO 標號
返回值: 非負值,得到的 GPIO 值;負值,獲取失敗
6.gpio_set_value 函數
此函數用於設置某個 GPIO 的值,此函數是個宏,定義如下
#define gpio_set_value __gpio_set_value void __gpio_set_value(unsigned gpio, int value)
函數參數和返回值含義如下:
gpio: 要設置的 GPIO 標號。
value: 要設置的值。
返回值: 無
設備樹中添加gpio節點模塊
- 創建test設備節點
在根節點“/”下創建test設備子節點,如下所示:test { /*節點內容*/ };
- 添加pinctrl信息
在pinctrl子系統的筆記中,創建了pinctrl_test節點,此節點描述了test設備所使用的GPIO1_IO00 這個 PIN 的信息,我們要將這節點添加到 test 設備節點中,如下所示:
test {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
/* 其他節點內容 */
};
- 添加GPIO屬性信息
我們最後需要在 test 節點中添加 GPIO 屬性信息,表明 test 所使用的 GPIO 是哪個引腳,添加完成以後如下所示:
test {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;
};
與 gpio 相關的 OF 函數
我們定義了一個名爲“gpio”的屬性, gpio 屬性描述了 test 這個設備所使用的 GPIO。在驅動程序中需要讀取 gpio 屬性內容, Linux 內核提供了幾個與 GPIO 有關的 OF 函數,常用的幾個 OF 函數如下所示:
- of_gpio_named_count 函數
- of_gpio_count 函數
- of_get_named_gpio 函數
1.of_gpio_named_count 函數
of_gpio_named_count 函數用於獲取設備樹某個屬性裏面定義了幾個 GPIO 信息,要注意的是空的 GPIO 信息也會被統計到,比如:
gpios = <0
&gpio1 1 2
0 &
gpio2 3 4>;
上述代碼的“gpios”節點一共定義了 4 個 GPIO,但是有 2 個是空的,沒有實際的含義。通過 of_gpio_named_count 函數統計出來的 GPIO 數量就是 4 個,此函數原型如下:
int of_gpio_named_count(struct device_node *np, const char *propname)
函數參數和返回值含義如下:
np:設備節點。
propname:要統計的 GPIO 屬性。
返回值: 正值,統計到的 GPIO 數量;負值,失敗。
2. of_gpio_count 函數
和 of_gpio_named_count 函數一樣,但是不同的地方在於,此函數統計的是“gpios”這個性的 GPIO 數量,而 of_gpio_named_count 函數可以統計任意屬性的 GPIO 信息,函數原型如下所示:
int of_gpio_count(struct device_node *np)
函數參數和返回值含義如下:
np:設備節點。
返回值: 正值,統計到的 GPIO 數量;負值,失敗
3.of_get_named_gpio 函數
此函數獲取 GPIO 編號,因爲 Linux 內核中關於 GPIO 的 API 函數都要使用 GPIO 編號,此函數會將設備樹中類似<&gpio5 7 GPIO_ACTIVE_LOW>的屬性信息轉換爲對應的 GPIO 編號,此函數在驅動中使用很頻繁!函數原型如下:
int of_get_named_gpio(struct device_node *np,const char *propname,int index)
函數參數和返回值含義如下:
np:設備節點。
propname:包含要獲取 GPIO 信息的屬性名。
index: GPIO 索引,因爲一個屬性裏面可能包含多個 GPIO,此參數指定要獲取哪個 GPIO的編號,如果只有一個 GPIO 信息的話此參數爲 0。
返回值: 正值,獲取到的 GPIO 編號;負值,失敗。