Linux GPIO 開發(fā)指南
1 概述
1.1 編寫目的
本文檔對內核的 GPIO 接口使用進行詳細的闡述,讓用戶明確掌握 GPIO 配置、申請等操作的編程方法。
1.2 適用范圍
表 1-1: 適用產品列表
內核版本 | 驅動文件 |
---|---|
Linux-4.9 及以上 | pinctrl-sunxi.c |
1.3 相關人員
本文檔適用于所有需要在 Linux 內核 sunxi 平臺上開發(fā)設備驅動的相關人員。
2 模塊介紹
Pinctrl 框架是 linux 系統(tǒng)為統(tǒng)一各 SoC 廠商 pin 管理,避免各 SoC 廠商各自實現(xiàn)相同 pin 管理子系統(tǒng)而提出的。目的是為了減少 SoC 廠商系統(tǒng)移植工作量。
2.1 模塊功能介紹
許多 SoC 內部都包含 pin 控制器,通過 pin 控制器,我們可以配置一個或一組引腳的功能和特性。在軟件上,Linux 內核 pinctrl 驅動可以操作 pin 控制器為我們完成如下工作:
? 枚舉并且命名 pin 控制器可控制的所有引腳;
? 提供引腳的復用能力
? 提供配置引腳的能力,如驅動能力、上拉下拉、數(shù)據屬性等。
? 與 gpio 子系統(tǒng)的交互
? 實現(xiàn) pin 中斷
2.2 相關術語介紹
表 2-1: Pinctrl 模塊相關術語介紹
術語 | 解釋說明 |
---|---|
SUNXI | Allwinner 一系列 SOC 硬件平臺 |
Pin controller | 是對硬件模塊的軟件抽象,通常用來表示硬件控制器。能夠處理引腳復用、屬性配置等功能 |
Pin | 根據芯片不同的封裝方式,可以表現(xiàn)為球形、針型等。軟件上采用常用一組無符號的整數(shù) [0-maxpin] 來表示 |
Pin groups | 外圍設備通常都不只一個引腳,比如 SPI,假設接在 SoC 的 {0,8,16,24} 管腳,而另一個設備 I2C 接在 SoC 的 {24,25} 管腳。我們可以說這里有兩個pin groups。很多控制器都需要處理 pin groups。因此管腳控制器子系統(tǒng)需要一個機制用來枚舉管腳組且檢索一個特定組中實際枚舉的管腳 |
Pinconfig | 管腳可以被軟件配置成多種方式,多數(shù)與它們作為輸入/輸出時的電氣特性相關。例如,可以設置一個輸出管腳處于高阻狀態(tài),或是 “三態(tài)”(意味著它被有效地斷開連接)?;蛘呖梢酝ㄟ^設置將一個輸入管腳與 VDD 或 GND 相連 (上拉/下拉),以便在沒有信號驅動管腳時使管腳擁有確認值 |
Pinmux | 引腳復用功能,使用一個特定的物理管腳(ball/pad/finger/等等)進行多種擴展復用,以支持不同功能的電氣封裝習慣 |
Device tree | 猶如它的名字,是一棵包括 cpu 的數(shù)量和類別、內存基地址、總線與橋、外設連接,中斷控制器和 gpio 以及 clock 等系統(tǒng)資源的樹,Pinctrl 驅動支持從device tree 中定義的設備節(jié)點獲取 pin 的配置信息 |
2.3 總體框架
Sunxi Pinctrl 驅動模塊的框架如下圖所示,整個驅動模塊可以分成 4 個部分:pinctrl api、pinctrl common frame、sunxi pinctrl driver,以及 board configuration。(圖中最上面一層 device driver 表示 Pinctrl 驅動的使用者)
圖 2-1: pinctrl 驅動整體框架圖
Pinctrl api: pinctrl 提供給上層用戶調用的接口。
Pinctrl framework:Linux 提供的 pinctrl 驅動框架。
Pinctrl sunxi driver:sunxi 平臺需要實現(xiàn)的驅動。
Board configuration:設備 pin 配置信息,一般采用設備樹進行配置。
2.4 state/pinmux/pinconfig
Pinctrl framework 主要處理 pinstate、pinmux 和 pinconfig 三個功能,pinstate 和 pinmux、pinconfig 映射關系如下圖所示。
圖 2-2: pinctrl 驅動 framework 圖
系統(tǒng)運行在不同的狀態(tài),pin 配置有可能不一樣,比如系統(tǒng)正常運行時,設備的 pin 需要一組配置,但系統(tǒng)進入休眠時,為了節(jié)省功耗,設備 pin 需要另一組配置。Pinctrl framwork 能夠有效管理設備在不同狀態(tài)下的引腳配置。
2.5 源碼結構介紹
linux
|
|-- drivers
| |-- pinctrl
| | |-- Kconfig
| | |-- Makefile
| | |-- core.c
| | |-- core.h
| | |-- devicetree.c
| | |-- devicetree.h
| | |-- pinconf.c
| | |-- pinconf.h
| | |-- pinmux.c
| | `-- pinmux.h
| `-- sunxi
| |-- pinctrl-sunxi-test.c
| |-- pinctrl-sun*.c
| `-- pinctrl-sun*-r.c
`-- include
`-- linux
`-- pinctrl
|-- consumer.h
|-- devinfo.h
|-- machine.h
|-- pinconf-generic.h
|-- pinconf.h
|-- pinctrl-state.h
|-- pinctrl.h
`-- pinmux.h
3 模塊配置
3.1 kernel menuconfig 配置
進入 longan 根目錄,執(zhí)行./build.sh menuconfig
進入配置主界面,并按以下步驟操作:
首先,選擇 Device Drivers 選項進入下一級配置,如下圖所示:
圖 3-1: 內核 menuconfig 根菜單
選擇 Pin controllers, 進入下級配置,如下圖所示:
圖 3-2: 內核 menuconfig device drivers 菜單
選擇 Allwinner SoC PINCTRL DRIVER, 進入下級配置,如下圖所示:
圖 3-3: 內核 menuconfig pinctrl drivers 菜單
Sunxi pinctrl driver 默認編譯進內核,如下圖(以 sun50iw9p1 平臺為例,其他平臺類似)所示:
圖 3-4: 內核 menuconfig allwinner pinctrl drivers 菜單
3.2 device tree 源碼結構和路徑
對于 Linux4.9:
? 設備樹文件的配置是該 SoC 所有方案的通用配置,對于 ARM64 CPU 而言,設備樹的路徑為:kernel/{KERNEL}/arch/arm64/boot/dts/sunxi/sun*-pinctrl.dtsi。
? 設備樹文件的配置是該 SoC 所有方案的通用配置,對于 ARM32 CPU 而言,設備樹的路徑為:kernel/{KERNEL}/arch/arm32/boot/dts/sun*-pinctrl.dtsi。
? 板級設備樹 (board.dts) 路徑:/device/config/chips/{IC}/configs/{BOARD}/board.dts
device tree 的源碼結構關系如下:
board.dts
|--------sun*.dtsi
|------sun*-pinctrl.dtsi
|------sun*-clk.dtsi
對于 Linux5.4:
? 設備樹文件的配置是該 SoC 所有方案的通用配置,對于 ARM64 CPU 而言,5.4 內核中不再維護單獨的 pinctrl 的 dtsi,直接將 pin 的信息放在了:kernel/{KERNEL}/arch/arm32/boot/dts/sun*.dtsi
? 設備樹文件的配置是該 SoC 所有方案的通用配置,對于 ARM32 CPU 而言,5.4 內核中不再維護單獨的 pinctrl 的 dtsi,直接將 pin 的信息放在了:kernel/{KERNEL}/arch/arm32/boot/dts/sun*.dtsi
? 板級設備樹 (board.dts) 路徑:/device/config/chips/{IC}/configs/{BOARD}/board.dts
? device tree 的源碼包含關系如下:
board.dts
|--------sun*.dtsi
3.2.1 device tree 對 gpio 控制器的通用配置
在 kernel/{KERNEL}/arch/arm64/boot/dts/sunxi/sun-pinctrl.dtsi 文件中 (Linux5.4 直接放在 sun.dtsi 中),配置了該 SoC 的 pinctrl 控制器的通用配置信息,一般不建議修改,有 pinctrl 驅動維護者維護。目前,在 sunxi 平臺,我們根據電源域,注冊兩個 pinctrl 設備:r_pio 設 備 (PL0 后的所有 pin) 和 pio 設備 (PL0 前的所有 pin),兩個設備的通用配置信息如下:
r_pio: pinctrl@07022000 {
compatible = "allwinner,sun50iw9p1-r-pinctrl"; //兼容屬性,用于驅動和設備綁定
reg = <0x0 0x07022000 0x0 0x400>; //寄存器基地址0x07022000和范圍0x400
clocks = <&clk_cpurpio>; //r_pio設置使用的時鐘
device_type = "r_pio"; //設備類型屬性
gpio-controller; //表示是一個gpio控制器
interrupt-controller; //表示一個中斷控制器,不支持中斷可以刪除
#interrupt-cells = <3>; //pin中斷屬性需要配置的參數(shù)個數(shù),不支持中斷可以刪除
#size-cells = <0>; //沒有使用,配置0
#gpio-cells = <6>; //gpio屬性配置需要的參數(shù)個數(shù),對于linux-5.4為3
?
/*
* 以下配置為模塊使用的pin的配置,模塊通過引用相應的節(jié)點對pin進行操作
* 由于不同板級的pin經常改變,建議通過板級dts修改(參考下一小節(jié))
*/
s_rsb0_pins_a: s_rsb0@0 {
allwinner,pins = "PL0", "PL1";
allwinner,function = "s_rsb0";
allwinner,muxsel = <2>;
allwinner,drive = <2>;
allwinner,pull = <1>;
};
?
/*
* 以下配置為linux-5.4模塊使用pin的配置,模塊通過引用相應的節(jié)點對pin進行操作
* 由于不同板級的pin經常改變,建議將模塊pin的引用放到board dts中
*(類似pinctrl-0 = <&scr1_ph_pins>;),并使用scr1_ph_pins這種更有標識性的名字)。
*/
scr1_ph_pins: scr1-ph-pins {
pins = "PH0", "PH1";
function = "sim1";
drive-strength = <10>;
bias-pull-up;
};
};
?
pio: pinctrl@0300b000 {
compatible = "allwinner,sun50iw9p1-pinctrl"; //兼容屬性,用于驅動和設備綁定
reg = <0x0 0x0300b000 0x0 0x400>; //寄存器基地址0x0300b000和范圍0x400
interrupts = , /* AW1823_GIC_Spec: GPIOA: 83-32=51 */
,
,
,
,
,
; //該設備每個bank支持的中斷配置和gic中斷號,每個中斷號對應一個支持中斷的bank
device_type = "pio"; //設備類型屬性
clocks = <&clk_pio>, <&clk_losc>, <&clk_hosc>; //該設備使用的時鐘
gpio-controller; //表示是一個gpio控制器
interrupt-controller; //表示是一個中斷控制器
#interrupt-cells = <3>; //pin中斷屬性需要配置的參數(shù)個數(shù),不支持中斷可以刪除
#size-cells = <0>; //沒有使用
#gpio-cells = <6>; //gpio屬性需要配置的參數(shù)個數(shù),對于linux-5.4為3
/* takes the debounce time in usec as argument */
}
3.2.2 board.dts 板級配置
board.dts 用于保存每個板級平臺的設備信息 (如 demo 板、demo2.0 板等等),以 demo 板為例,board.dts 路徑如下:
/device/config/chips/{CHIP}/configs/demo/board.dts
在 board.dts 中的配置信息如果在 *.dtsi 中 (如 sun50iw9p1.dtsi 等) 存在,則會存在以下覆蓋規(guī)則:
? 相同屬性和結點,board.dts 的配置信息會覆蓋 *.dtsi 中的配置信息。
? 新增加的屬性和結點,會追加到最終生成的 dtb 文件中。
linux-4.9 上面 pinctrl 中一些模塊使用 board.dts 的簡單配置如下:
pio: pinctrl@0300b000 {
input-debounce = <0 0 0 0 0 0 0>; /*配置中斷采樣頻率,每個對應一個支持中斷的bank,單位us*/
spi0_pins_a: spi0@0 {
allwinner,pins = "PC0", "PC2", "PC4";
allwinner,pname = "spi0_sclk", "spi0_mosi", "spi0_miso";
allwinner,function = "spi0";
};
};
對于 linux-5.4,不建議采用上面的覆蓋方式,而是修改驅動 pinctrl-0 引用的節(jié)點。
linux-5.4 上面 board.dts 的配置如下:
&pio{
input-debounce = <0 0 0 0 1 0 0 0 0>; //配置中斷采樣頻率,每個對應一個支持中斷的bank,單位us
vcc-pe-supply = ; //配置IO口耐壓值,例如這里的含義是將pe口設置成1.8v耐壓值
};
4 模塊接口說明
4.1 pinctrl 接口說明
4.1.1 pin4ctrl_get
? 函數(shù)原型:struct pinctrl *pinctrl_get(struct device *dev);
? 作用:獲取設備的 pin 操作句柄,所有 pin 操作必須基于此 pinctrl 句柄。
? 參數(shù):
dev: 指向申請 pin 操作句柄的設備句柄。
? 返回:
成功,返回 pinctrl 句柄。
失敗,返回 NULL。
4.1.2 pinctrl_put
? 函數(shù)原型:void pinctrl_put(struct pinctrl *p)
? 作用:釋放 pinctrl 句柄,必須與 pinctrl_get 配對使用。
? 參數(shù):
? p: 指向釋放的 pinctrl 句柄。
? 返回:
? 沒有返回值。
! 警告
必須與 pinctrl_get 配對使用。
4.1.3 devm_pinctrl_get
? 函數(shù)原型:struct pinctrl *devm_pinctrl_get(struct device *dev)
? 作用:根據設備獲取 pin 操作句柄,所有 pin 操作必須基于此 pinctrl 句柄,與 pinctrl_get功能完全一樣,只是 devm_pinctrl_get 會將申請到的 pinctrl 句柄做記錄,綁定到設備句柄信息中。設備驅動申請 pin 資源,推薦優(yōu)先使用 devm_pinctrl_get 接口。
? 參數(shù):
? dev: 指向申請 pin 操作句柄的設備句柄。
? 返回:
? 成功,返回 pinctrl 句柄。
? 失敗,返回 NULL。
4.1.4 devm_pinctrl_put
? 函數(shù)原型:void devm_pinctrl_put(struct pinctrl *p)
? 作用:釋放 pinctrl 句柄,必須與 devm_pinctrl_get 配對使用。
? 參數(shù):
? p: 指向釋放的 pinctrl 句柄。
? 返回:
? 沒有返回值。
! 警告
必須與 devm_pinctrl_get 配對使用,可以不顯式的調用該接口。
4.1.5 pinctrl_lookup_state
? 函數(shù)原型:struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)
? 作用:根據 pin 操作句柄,查找 state 狀態(tài)句柄。
? 參數(shù):
? p: 指向要操作的 pinctrl 句柄。
? name: 指向狀態(tài)名稱,如 “default”、“sleep” 等。
? 返回:
? 成功,返回執(zhí)行 pin 狀態(tài)的句柄 struct pinctrl_state *。
? 失敗,返回 NULL。
4.1.6 pinctrl_select_state
? 函數(shù)原型:int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s)
? 作用:將 pin 句柄對應的 pinctrl 設置為 state 句柄對應的狀態(tài)。
? 參數(shù):
? p: 指向要操作的 pinctrl 句柄。
? s: 指向 state 句柄。
? 返回:
? 成功,返回 0。
? 失敗,返回錯誤碼。
4.1.7 devm_pinctrl_get_select
? 函數(shù)原型:struct pinctrl *devm_pinctrl_get_select(struct device *dev, const char *name)
? 作用:獲取設備的 pin 操作句柄,并將句柄設定為指定狀態(tài)。
? 參數(shù):
? dev: 指向管理 pin 操作句柄的設備句柄。
? name: 要設置的 state 名稱,如 “default”、“sleep” 等。
? 返回:
? 成功,返回 pinctrl 句柄。
? 失敗,返回 NULL。
4.1.8 devm_pinctrl_get_select_default
? 函數(shù)原型:struct pinctrl *devm_pinctrl_get_select_default(struct device *dev)
? 作用:獲取設備的 pin 操作句柄,并將句柄設定為默認狀態(tài)。
? 參數(shù):
? dev: 指向管理 pin 操作句柄的設備句柄。
? 返回:
? 成功,返回 pinctrl 句柄。
? 失敗,返回 NULL。
4.1.9 pin_config_get
? 作用:獲取指定 pin 的屬性。
? 參數(shù):
? dev_name: 指向 pinctrl 設備。
? name: 指向 pin 名稱。
? config: 保存 pin 的配置信息。
? 返回:
? 成功,返回 pin 編號。
? 失敗,返回錯誤碼。
! 警告
該接口在 linux-5.4 已經移除。
4.1.10 pin_config_set
? 作用:設置指定 pin 的屬性。
? 參數(shù):
? dev_name: 指向 pinctrl 設備。
? name: 指向 pin 名稱。
? config:pin 的配置信息。
? 返回:
? 成功,返回 0。
? 失敗,返回錯誤碼。
! 警告
該接口在 linux-5.4 已經移除。
4.2 gpio 接口說明
4.2.1 gpio_request
? 函數(shù)原型:int gpio_request(unsigned gpio, const char *label)
? 作用:申請 gpio,獲取 gpio 的訪問權。
? 參數(shù):
? gpio:gpio 編號。
? label:gpio 名稱,可以為 NULL。
? 返回:
? 成功,返回 0。
? 失敗,返回錯誤碼。
4.2.2 gpio_free
? 函數(shù)原型:void gpio_free(unsigned gpio)
? 作用:釋放 gpio。
? 參數(shù):
? gpio:gpio 編號。
? 返回:
? 無返回值。
4.2.3 gpio_direction_input
? 函數(shù)原型:int gpio_direction_input(unsigned gpio)
? 作用:設置 gpio 為 input。
? 參數(shù):
? gpio:gpio 編號。
? 返回:
? 成功,返回 0。
? 失敗,返回錯誤碼。
4.2.5 __gpio_get_value
? 函數(shù)原型:int __gpio_get_value(unsigned gpio)
? 作用:獲取 gpio 電平值 (gpio 已為 input/output 狀態(tài))。
? 參數(shù):
? gpio:gpio 編號。
? 返回:
? 返回 gpio 對應的電平邏輯,1 表示高, 0 表示低。
4.2.6 __gpio_set_value
? 函數(shù)原型:void __gpio_set_value(unsigned gpio, int value)
? 作用:設置 gpio 電平值 (gpio 已為 input/output 狀態(tài))。
? 參數(shù):
? gpio:gpio 編號。
? value: 期望設置的 gpio 電平值,非 0 表示高, 0 表示低。
? 返回:
? 無返回值
4.2.7 of_get_named_gpio
? 函數(shù)原型:int of_get_named_gpio(struct device_node *np, const char *propname, int index)
? 作用:通過名稱從 dts 解析 gpio 屬性并返回 gpio 編號。
? 參數(shù):
? np: 指向使用 gpio 的設備結點。
? propname:dts 中屬性的名稱。
? index:dts 中屬性的索引值。
? 返回:
? 成功,返回 gpio 編號。
? 失敗,返回錯誤碼。
4.2.8 of_get_named_gpio_flags
? 函數(shù)原型:int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index,
enum of_gpio_flags *flags)
? 作用:通過名稱從 dts 解析 gpio 屬性并返回 gpio 編號。
? 參數(shù):
? np: 指向使用 gpio 的設備結點。
? propname:dts 中屬性的名稱。
? index:dts 中屬性的索引值
? flags: 在 sunxi 平臺上,必須定義為 struct gpio_config * 類型變量,因為 sunxi pinctrl的 pin 支持上下拉, 驅動能力等信息,而內核 enum of_gpio_flags * 類型變量只能包含輸入、輸出信息,后續(xù) sunxi 平臺 需要標準化該接口。
? 返回:
? 成功,返回 gpio 編號。
? 失敗,返回錯誤碼。
! 警告
該接口的 flags 參數(shù),在 sunxi linux-4.9 及以前的平臺上,必須定義為 struct gpio_config 類型變量。linux-5.4 已經標準化該接口,直接采用 enum of_gpio_flags 的定義。
5 使用示例
5.1 使用 pin 的驅動 dts 配置示例
對于使用 pin 的驅動來說,驅動主要設置 pin 的常用的幾種功能,列舉如下:
? 驅動使用者只配置通用 GPIO, 即用來做輸入、輸出和中斷的
? 驅動使用者設置 pin 的 pin mux,如 uart 設備的 pin,lcd 設備的 pin 等,用于特殊功能
? 驅動使用者既要配置 pin 的通用功能,也要配置 pin 的特性
下面對常見使用場景進行分別介紹。
5.1.1 配置通用 GPIO 功能/中斷功能
用法一:配置 GPIO,中斷,device tree 配置 demo 如下所示:
soc{
...
gpiokey {
device_type = "gpiokey";
compatible = "gpio-keys";
ok_key {
device_type = "ok_key";
label = "ok_key";
gpios = <&r_pio PL 0x4 0x0 0x1 0x0 0x1>; //如果是linux-5.4,則應該為gpios = <&r_pio 0 4 GPIO_ACTIVE_HIGH>;
linux,input-type = "1>";
linux,code = <0x1c>;
wakeup-source = <0x1>;
};
};
...
};
說明
說明:gpio in/gpio out/ interrupt采用dts的配置方法,配置參數(shù)解釋如下:
對于linux-4.9:
gpios = <&r_pio PL 0x4 0x0 0x1 0x0 0x1>;
| | | | | | `---輸出電平,只有output才有效
| | | | | `-------驅動能力,值為0x0時采用默認值
| | | | `-----------上下拉,值為0x1時采用默認值
| | | `---------------復用類型
| | `-------------------當前bank中哪個引腳
| `-----------------------哪個bank
`---------------------------指向哪個pio,屬于cpus要用&r_pio
使用上述方式配置gpio時,需要驅動調用以下接口解析dts的配置參數(shù):
int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index,
enum of_gpio_flags *flags)
拿到gpio的配置信息后(保存在flags參數(shù)中,見4.2.8.小節(jié)),在根據需要調用相應的標準接口實現(xiàn)自己的功能
對于linux-5.4:
gpios = <&r_pio 0 4 GPIO_ACTIVE_HIGH>;
| | |
| | `-------------------gpio active時狀態(tài),如果需要上下拉,還可以或上
GPIO_PULL_UP、GPIO_PULL_DOWN標志
| `-----------------------哪個bank
`---------------------------指向哪個pio,屬于cpus要用&r_pio
5.1.2 用法二
用法二:配置設備引腳,device tree 配置 demo 如下所示:
device tree對應配置 soc{ pio: pinctrl@0300b000 { ... uart0_ph_pins_a: uart0-ph-pins-a { allwinner,pins = "PH7", "PH8"; allwinner,function = "uart0"; allwinner,muxsel = <3>; allwinner,drive = <0x1>; allwinner,pull = <0x1>; }; /* 對于linux-5.4 請使用下面這種方式配置 */ mmc2_ds_pin: mmc2-ds-pin { pins = "PC1"; function = "mmc2"; drive-strength = <30>; bias-pull-up; }; ... }; ... uart0: uart@05000000 { compatible = "allwinner,sun8i-uart"; device_type = "uart0"; reg = <0x0 0x05000000 0x0 0x400>; interrupts = ; clocks = <&clk_uart0>; pinctrl-names = "default", "sleep"; pinctrl-0 = <&uart0_pins_a>; pinctrl-1 = <&uart0_pins_b>; uart0_regulator = "vcc-io"; uart0_port = <0>; uart0_type = <2>; }; ... };
其中:
? pinctrl-0 對應 pinctrl-names 中的 default,即模塊正常工作模式下對應的 pin 配置
? pinctrl-1 對應 pinctrl-names 中的 sleep,即模塊休眠模式下對應的 pin 配置
5.2 接口使用示例
5.2.1 配置設備引腳
一般設備驅動只需要使用一個接口 devm_pinctrl_get_select_default 就可以申請到設備所有pin 資源。
static int sunxi_pin_req_demo(struct platform_device *pdev) { struct pinctrl *pinctrl; /* request device pinctrl, set as default state */ pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR_OR_NULL(pinctrl)) return -EINVAL; return 0; }
5.2.2 獲取 GPIO 號
static int sunxi_pin_req_demo(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; unsigned int gpio; #get gpio config in device node. gpio = of_get_named_gpio(np, "vdevice_3", 0); if (!gpio_is_valid(gpio)) { if (gpio != -EPROBE_DEFER) dev_err(dev, "Error getting vdevice_3n"); return gpio; } }
5.2.3 GPIO 屬性配置
通過 pin_config_set/pin_config_get/pin_config_group_set/pin_config_group_get 接口單獨控制指定 pin 或 group 的相關屬性。
static int pctrltest_request_all_resource(void) { struct device *dev; struct device_node *node; struct pinctrl *pinctrl; struct sunxi_gpio_config *gpio_list = NULL; struct sunxi_gpio_config *gpio_cfg; unsigned gpio_count = 0; unsigned gpio_index; unsigned long config; int ret; dev = bus_find_device_by_name(&platform_bus_type, NULL, sunxi_ptest_data->dev_name); if (!dev) { pr_warn("find device [%s] failed...n", sunxi_ptest_data->dev_name); return -EINVAL; } node = of_find_node_by_type(NULL, dev_name(dev)); if (!node) { pr_warn("find node for device [%s] failed...n", dev_name(dev)); return -EINVAL; } dev->of_node = node; pr_warn("++++++++++++++++++++++++++++%s++++++++++++++++++++++++++++n", __func__); pr_warn("device[%s] all pin resource we want to requestn", dev_name(dev)); pr_warn("-----------------------------------------------n"); pr_warn("step1: request pin all resource.n"); pinctrl = devm_pinctrl_get_select_default(dev); if (IS_ERR_OR_NULL(pinctrl)) { pr_warn("request pinctrl handle for device [%s] failed...n", dev_name(dev)); return -EINVAL; } pr_warn("step2: get device[%s] pin count.n", dev_name(dev)); ret = dt_get_gpio_list(node, &gpio_list, &gpio_count); if (ret < 0 || gpio_count == 0) { ? ? ? ?pr_warn(" devices own 0 pin resource or look for main key failed!n"); ? ? ? ?return -EINVAL; ? ?} ? ?pr_warn("step3: get device[%s] pin configure and check.n", dev_name(dev)); ? ?for (gpio_index = 0; gpio_index < gpio_count; gpio_index++) { ? ? ? ?gpio_cfg = &gpio_list[gpio_index]; ? ? ? ?/*check function config */ ? ? ? ?config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0xFFFF); ? ? ? ?pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config); if (gpio_cfg->mulsel != SUNXI_PINCFG_UNPACK_VALUE(config)) { pr_warn("failed! mul value isn't equal as dt.n"); return -EINVAL; } /*check pull config */ if (gpio_cfg->pull != GPIO_PULL_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0xFFFF); pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config); if (gpio_cfg->pull != SUNXI_PINCFG_UNPACK_VALUE(config)) { pr_warn("failed! pull value isn't equal as dt.n"); return -EINVAL; } } /*check dlevel config */ if (gpio_cfg->drive != GPIO_DRVLVL_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV, 0XFFFF); pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config); if (gpio_cfg->drive != SUNXI_PINCFG_UNPACK_VALUE(config)) { pr_warn("failed! dlevel value isn't equal as dt.n"); return -EINVAL; } } /*check data config */ if (gpio_cfg->data != GPIO_DATA_DEFAULT) { config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF); pin_config_get(SUNXI_PINCTRL, gpio_cfg->name, &config); if (gpio_cfg->data != SUNXI_PINCFG_UNPACK_VALUE(config)) { pr_warn("failed! pin data value isn't equal as dt.n"); return -EINVAL; } } } pr_warn("-----------------------------------------------n"); pr_warn("test pinctrl request all resource success!n"); pr_warn("++++++++++++++++++++++++++++end++++++++++++++++++++++++++++nn"); return 0; } 注:需要注意,存在SUNXI_PINCTRL和SUNXI_R_PINCTRL兩個pinctrl設備,cpus域的pin需要使用 SUNXI_R_PINCTRL
! 警告
linux5.4 中 使 用 pinctrl_gpio_set_config 配 置 gpio 屬 性, 對 應 使 用pinconf_to_config_pack 生成 config 參數(shù):
? SUNXI_PINCFG_TYPE_FUNC 已不再生效,暫未支持 FUNC 配置(建議使用 pinctrl_select_state接口代替)
? SUNXI_PINCFG_TYPE_PUD 更新為內核標準定義(PIN_CONFIG_BIAS_PULL_UP/PIN_CONFIG_BIAS_PULL_DOWN)
? SUNXI_PINCFG_TYPE_DRV 更新為內核標準定義(PIN_CONFIG_DRIVE_STRENGTH),相應的 val 對應關系為(4.9->5.4: 0->10, 1->20…)
? SUNXI_PINCFG_TYPE_DAT 已不再生效,暫未支持 DAT 配置(建議使用 gpio_direction_output或者 __gpio_set_value 設置電平值)
5.3 設備驅動使用 GPIO 中斷功能
方式一:通過 gpio_to_irq 獲取虛擬中斷號,然后調用申請中斷函數(shù)即可目前 sunxi-pinctrl 使用 irq-domain 為 gpio 中斷實現(xiàn)虛擬 irq 的功能,使用 gpio 中斷功能時,設備驅動只需要通過 gpio_to_irq 獲取虛擬中斷號后,其他均可以按標準 irq 接口操作。
static int sunxi_gpio_eint_demo(struct platform_device *pdev) { struct device *dev = &pdev->dev; int virq; int ret; /* map the virq of gpio */ virq = gpio_to_irq(GPIOA(0)); if (IS_ERR_VALUE(virq)) { pr_warn("map gpio [%d] to virq failed, errno = %dn", GPIOA(0), virq); return -EINVAL; } pr_debug("gpio [%d] map to virq [%d] okn", GPIOA(0), virq); /* request virq, set virq type to high level trigger */ ret = devm_request_irq(dev, virq, sunxi_gpio_irq_test_handler, IRQF_TRIGGER_HIGH, "PA0_EINT", NULL); if (IS_ERR_VALUE(ret)) { pr_warn("request virq %d failed, errno = %dn", virq, ret); return -EINVAL; } return 0; }
方式二:通過 dts 配置 gpio 中斷,通過 dts 解析函數(shù)獲取虛擬中斷號,最后調用申請中斷函數(shù)即可,demo 如下所示:
dts配置如下: soc{ ... Vdevice: vdevice@0 { compatible = "allwinner,sun8i-vdevice"; device_type = "Vdevice"; interrupt-parent = <&pio>; /*依賴的中斷控制器(帶interrupt-controller屬性的結 點)*/ interrupts = < PD 3 IRQ_TYPE_LEVEL_HIGH>; | | `------------------中斷觸發(fā)條件、類型 | `-------------------------pin bank內偏移 `---------------------------哪個bank pinctrl-names = "default"; pinctrl-0 = <&vdevice_pins_a>; test-gpios = <&pio PC 3 1 2 2 1>; status = "okay"; }; ... };
在驅動中,通過 platform_get_irq() 標準接口獲取虛擬中斷號,如下所示:
static int sunxi_pctrltest_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct gpio_config config; int gpio, irq; int ret; if (np == NULL) { pr_err("Vdevice failed to get of_noden"); return -ENODEV; } .... irq = platform_get_irq(pdev, 0); if (irq < 0) { ? ? ? ?printk("Get irq error!n"); ? ? ? ?return -EBUSY; ? ?} ..... sunxi_ptest_data->irq = irq; ...... return ret; } //申請中斷: static int pctrltest_request_irq(void) { int ret; int virq = sunxi_ptest_data->irq; int trigger = IRQF_TRIGGER_HIGH; reinit_completion(&sunxi_ptest_data->done); pr_warn("step1: request irq(%s level) for irq:%d.n", trigger == IRQF_TRIGGER_HIGH ? "high" : "low", virq); ret = request_irq(virq, sunxi_pinctrl_irq_handler_demo1, trigger, "PIN_EINT", NULL); if (IS_ERR_VALUE(ret)) { pr_warn("request irq failed !n"); return -EINVAL; } pr_warn("step2: wait for irq.n"); ret = wait_for_completion_timeout(&sunxi_ptest_data->done, HZ); if (ret == 0) { pr_warn("wait for irq timeout!n"); free_irq(virq, NULL); return -EINVAL; } free_irq(virq, NULL); pr_warn("-----------------------------------------------n"); pr_warn("test pin eint success !n"); pr_warn("+++++++++++++++++++++++++++end++++++++++++++++++++++++++++nnn"); return 0; }
5.4 設備驅動設置中斷 debounce 功能
方式一:通過 dts 配置每個中斷 bank 的 debounce,以 pio 設備為例,如下所示:
&pio { /* takes the debounce time in usec as argument */ input-debounce = <0 0 0 0 0 0 0>; | | | | | | `----------PA bank | | | | | `------------PC bank | | | | `--------------PD bank | | | `----------------PF bank | | `------------------PG bank | `--------------------PH bank `----------------------PI bank };
注意:input-debounce 的屬性值中需把 pio 設備支持中斷的 bank 都配上,如果缺少,會以bank 的順序設置相應的屬性值到 debounce 寄存器,缺少的 bank 對應的 debounce 應該是默認值(啟動時沒修改的情況)。sunxi linux-4.9 平臺,中斷采樣頻率最大是 24M, 最小 32k,debounce 的屬性值只能為 0 或 1。對于 linux-5.4,debounce 取值范圍是 0~1000000(單位 usec)。
方式二:驅動模塊調用 gpio 相關接口設置中斷 debounce
static inline int gpio_set_debounce(unsigned gpio, unsigned debounce); int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
在驅動中,調用上面兩個接口即可設置 gpio 對應的中斷 debounce 寄存器,注意,debounce 是以 ms 為單位的 (linux-5.4 已經移除這個接口)。
6 FAQ
6.1 常用 debug 方法
6.1.1 利用 sunxi_dump 讀寫相應寄存器
需要開啟 SUNXI_DUMP 模塊:
make kernel_menuconfig ---> Device Drivers ---> dump reg driver for sunxi platform (選中)
使用方法:
cd /sys/class/sunxi_dump 1.查看一個寄存器 echo 0x0300b048 > dump ;cat dump 2.寫值到寄存器上 echo 0x0300b058 0xfff > write ;cat write 3.查看一片連續(xù)寄存器 echo 0x0300b000,0x0300bfff > dump;cat dump 4.寫一組寄存器的值 echo 0x0300b058 0xfff,0x0300b0a0 0xfff > write;cat write 通過上述方式,可以查看,修改相應gpio的寄存器,從而發(fā)現(xiàn)問題所在。
6.1.2 利用 sunxi_pinctrl 的 debug 節(jié)點
需要開啟 DEBUG_FS:
make kernel_menuconfig ---> Kernel hacking ---> Compile-time checks and compiler options ---> Debug Filesystem (選中)
掛載文件節(jié)點,并進入相應目錄:
mount -t debugfs none /sys/kernel/debug cd /sys/kernel/debug/sunxi_pinctrl
1.查看 pin 的配置:
echo PC2 > sunxi_pin cat sunxi_pin_configure
結果如下圖所示:
圖 6-1: 查看 pin 配置圖
2.修改 pin 屬性
每個 pin 都有四種屬性,如復用 (function),數(shù)據 (data),驅動能力 (dlevel),上下拉 (pull),
修改 pin 屬性的命令如下:
echo PC2 1 > pull;cat pull cat sunxi_pin_configure //查看修改情況
修改后結果如下圖所示:
圖 6-2: 修改結果圖
注意:在 sunxi 平臺,目前多個 pinctrl 的設備,分別是 pio 和 r_pio 和 axpxxx-gpio,當操作 PL 之后的 pin 時,請通過以下命令切換 pin 的設備,否則操作失敗,切換命令如下:
echo pio > /sys/kernel/debug/sunxi_pinctrl/dev_name //切換到pio設備 cat /sys/kernel/debug/sunxi_pinctrl/dev_name echo r_pio > /sys/kernel/debug/sunxi_pinctrl/dev_name //切換到r_pio設備 cat /sys/kernel/debug/sunxi_pinctrl/dev_name
修改結果如下圖所示:
圖 6-3: pin 設備圖
6.1.3 利用 pinctrl core 的 debug 節(jié)點
mount -t debugfs none /sys/kernel/debug cd /sys/kernel/debug/sunxi_pinctrl
1.查看 pin 的管理設備:
cat pinctrl-devices
結果如下圖所示:
圖 6-4: pin 設備圖
2.查看 pin 的狀態(tài)和對應的使用設備
結果如下圖 log 所示:
console:/sys/kernel/debug/pinctrl # ls pinctrl-devices pinctrl-handles pinctrl-maps pio r_pio console:/sys/kernel/debug/pinctrl # cat pinctrl-handles Requested pin control handlers their pinmux maps: device: twi3 current state: sleep state: default type: MUX_GROUP controller pio group: PA10 (10) function: twi3 (15) type: CONFIGS_GROUP controller pio group PA10 (10)config 00001409 config 00000005 type: MUX_GROUP controller pio group: PA11 (11) function: twi3 (15) type: CONFIGS_GROUP controller pio group PA11 (11)config 00001409 config 00000005 state: sleep type: MUX_GROUP controller pio group: PA10 (10) function: io_disabled (5) type: CONFIGS_GROUP controller pio group PA10 (10)config 00001409 config 00000001 type: MUX_GROUP controller pio group: PA11 (11) function: io_disabled (5) type: CONFIGS_GROUP controller pio group PA11 (11)config 00001409 config 00000001 device: twi5 current state: default state: default type: MUX_GROUP controller r_pio group: PL0 (0) function: s_twi0 (3) type: CONFIGS_GROUP controller r_pio group PL0 (0)config 00001409 config 00000005 type: MUX_GROUP controller r_pio group: PL1 (1) function: s_twi0 (3) type: CONFIGS_GROUP controller r_pio group PL1 (1)config 00001409 config 00000005 state: sleep type: MUX_GROUP controller r_pio group: PL0 (0) function: io_disabled (4) type: CONFIGS_GROUP controller r_pio group PL0 (0)config 00001409 config 00000001 type: MUX_GROUP controller r_pio group: PL1 (1) function: io_disabled (4) type: CONFIGS_GROUP controller r_pio group PL1 (1)config 00001409 config 00000001 device: soc@03000000:pwm5@0300a000 current state: active state: active type: MUX_GROUP controller pio group: PA12 (12) function: pwm5 (16) type: CONFIGS_GROUP controller pio group PA12 (12)config 00000001 config 00000000 config 00000000 state: sleep type: MUX_GROUP controller pio group: PA12 (12) function: io_disabled (5) type: CONFIGS_GROUP controller pio group PA12 (12)config 00000001 config 00000000 config 00000000 device: uart0 current state: default state: default state: sleep device: uart1 current state: default state: default type: MUX_GROUP controller pio group: PG6 (95) function: uart1 (37) type: CONFIGS_GROUP controller pio group PG6 (95)config 00001409 config 00000005 type: MUX_GROUP controller pio group: PG7 (96) function: uart1 (37) type: CONFIGS_GROUP controller pio group PG7 (96)config 00001409 config 00000005 type: MUX_GROUP controller pio group: PG8 (97) function: uart1 (37) type: CONFIGS_GROUP controller pio group PG8 (97)config 00001409 config 00000005 type: MUX_GROUP controller pio group: PG9 (98) function: uart1 (37) type: CONFIGS_GROUP controller pio group PG9 (98)config 00001409 config 00000005 state: sleep type: MUX_GROUP controller pio group: PG6 (95) function: io_disabled (5) type: CONFIGS_GROUP controller pio group PG6 (95)config 00001409 config 00000001 type: MUX_GROUP controller pio group: PG7 (96) function: io_disabled (5) type: CONFIGS_GROUP controller pio group PG7 (96)config 00001409 config 00000001 type: MUX_GROUP controller pio group: PG8 (97) function: io_disabled (5) type: CONFIGS_GROUP controller pio group PG8 (97)config 00001409 config 00000001 type: MUX_GROUP controller pio group: PG9 (98) function: io_disabled (5) type: CONFIGS_GROUP controller pio group PG9 (98)config 00001409 ....
從上面的部分 log 可以看到那些設備管理的 pin 以及 pin 當前的狀態(tài)是否正確。以 twi3 設備為例,twi3 管理的 pin 有 PA10/PA11,分別有兩組狀態(tài) sleep 和 default,default 狀態(tài)表示使用狀態(tài),sleep 狀態(tài)表示 pin 處于 io disabled 狀態(tài),表示 pin 不可正常使用,twi3 設備使用的 pin 當前狀態(tài)處于 sleep 狀態(tài)的。
6.1.4 GPIO 中斷問題排查步驟
6.1.4.1 GPIO 中斷一直響應
排查中斷信號是否一直觸發(fā)中斷
利用 sunxi_dump 節(jié)點,確認中斷 pending 位是否沒有清 (參考 6.1.1 小節(jié))
是否在 gpio 中斷服務程序里對中斷檢測的 gpio 進行 pin mux 的切換,不允許這樣切換,否則會導致中斷異常
6.1.4.2 GPIO 檢測不到中斷
排查中斷信號是否正常,若不正常,則排查硬件,若正常,則跳到步驟 2
利用 sunxi_dump 節(jié)點,查看 gpio 中斷 pending 位是否置起,若已經置起,則跳到步驟5,否則跳到步驟 3
利用 sunxi_dump 節(jié)點,查看 gpio 的中斷觸發(fā)方式是否配置正確,若正確,則跳到步驟 4,否則跳到步驟 5
檢查中斷的采樣時鐘,默認應該是 32k,可以通過 sunxi_dump 節(jié)點,切換 gpio 中斷采樣時鐘到 24M 進行實驗
利用 sunxi_dump,確認中斷是否使能
審核編輯:湯梓紅
-
接口
+關注
關注
33文章
8504瀏覽量
150843 -
Linux
+關注
關注
87文章
11230瀏覽量
208937 -
GPIO
+關注
關注
16文章
1196瀏覽量
51920
發(fā)布評論請先 登錄
相關推薦
評論