最近在玩瑞芯微平臺的產(chǎn)品,移植了幾個設(shè)備的驅(qū)動,遇到了一些問題,總結(jié)后發(fā)現(xiàn)大部分問題都出在了GPIO配置的問題上,寫下本篇文章,用來分享一下調(diào)試的心得。
有喜歡瑞芯微的朋友,可以加我好友,拉你進(jìn)群,一起學(xué)習(xí)進(jìn)步。
0、前言
本文基于rk3568平臺。
要查看rk3568 GPIO分組及地址信息,需要查看TRM手冊,下載地址:http://opensource.rock-chips.com
也可以公-眾-號后臺回復(fù):rxw可獲取更豐富的資料。
一、RXW-GPIO介紹
GPIO(General Purpose Input/Output Port):通用輸入輸出端口。
除作為一般的輸入/輸出功能外,還可以配置為中斷和模擬UART、CAN、PWM、I2C、SDMMC、CLK等功能。
1. GPIO分組
一共有5組GPIO(GPIO0~4),每組GPIO為一個Bank,共32個引腳。每個Bank包括4個 **Group (GPIOA(0~7) ~ D( 0~7)) **。RK3568共160個GPIO引腳。
GRF(General Register Files)做了分組,
?PMU_GRF,?used?for?always?on?logic?control
?CPU_GRF,?used?for?always?on?system
?DDR_GRF,?used?for?DDR?system
?PIPE_GRF,?used?for?pipe?interface?controller,
?SYS_GRF,?used?for?general?system
?PIPEPHY_GRF,used?for?pipe?interface?phy
?USBPHY_U3_GRF,used?for?usb3?phy
?USBPHY_U2_GRF,used?for?usb2?phy
?EDP_PHY_GRF,used?for?eDP?PHY?control
?PCIEPHY_GRF,used?for?pcie3.0?phy
?USB_GRF,used?for?usb2?host?controller
要查找GPIO對應(yīng)的配置寄存器地址,必須知道他屬于哪個分組:
2. GPIO引腳號計算方式:
pins?=?32*bank_num?+?8*group?+?x
bank_num?:?0?~?4,對應(yīng)GPIO?0~4
group????:?0?~?3,對應(yīng)GPIO?A~D
例如GPIO2 A2:
GPIO2_A2?=?32*2?+?8*0?+?2?=?66
由上圖可得:
- gpio0 bank屬于PMU分組, 基地址:0xFDC20000gpio0~4 bank 屬于SYS分組,基地址:0xFDC60000GPIO0 bank控制pin0~31GPIO1 bank控制pin32~63GPIO2 bank控制pin64~95GPIO3 bank控制pin96~127GPIO4 bank控制pin128~159
通過上圖,很方便查找到對應(yīng)的GPIO引腳號以及IOMUX control寄存器地址。一口君還畫了下面這個圖,大家根據(jù)自己喜好,看看哪一個比較好:
3. sys文件查看pin與gpio號之間映射
也可以用debugfs來查看pin與gpio號之間映射關(guān)系
rk3568_r:/sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl?#?cat?pins
cat?pins
registered?pins:?160
##?GPIO?0_*包括的GPIO
pin?0?(gpio0-0)
pin?1?(gpio0-1)
pin?2?(gpio0-2)
pin?3?(gpio0-3)
pin?4?(gpio0-4)
pin?5?(gpio0-5)
pin?6?(gpio0-6)
pin?7?(gpio0-7)
pin?8?(gpio0-8)
……
##?GPIO?1_*包括的GPIO
pin?32?(gpio1-0)
pin?33?(gpio1-1)
pin?34?(gpio1-2)
pin?35?(gpio1-3)
pin?36?(gpio1-4)
pin?37?(gpio1-5)
pin?38?(gpio1-6)
pin?39?(gpio1-7)
pin?40?(gpio1-8)
……
##?GPIO?2_*包括的GPIO
pin?64?(gpio2-0)
pin?65?(gpio2-1)
pin?66?(gpio2-2)
pin?67?(gpio2-3)
pin?68?(gpio2-4)
pin?69?(gpio2-5)
pin?70?(gpio2-6)
pin?71?(gpio2-7)
pin?72?(gpio2-8)
……
##?GPIO?3_*包括的GPIO
pin?96?(gpio3-0)
pin?97?(gpio3-1)
pin?98?(gpio3-2)
pin?99?(gpio3-3)
pin?100?(gpio3-4)
pin?101?(gpio3-5)
pin?102?(gpio3-6)
pin?103?(gpio3-7)
pin?104?(gpio3-8)
……
##?GPIO?2_*包括的GPIO
pin?128?(gpio4-0)
pin?129?(gpio4-1)
pin?130?(gpio4-2)
pin?131?(gpio4-3)
pin?132?(gpio4-4)
pin?133?(gpio4-5)
pin?134?(gpio4-6)
pin?135?(gpio4-7)
pin?136?(gpio4-8)
……
pin?159?(gpio4-31)
二、rk3568GPIO控制器驅(qū)動
1. gpio相關(guān)api
Linux內(nèi)核GPIO主要實(shí)現(xiàn)文件:
drivers/gpio/gpiolib.c?????????##?新版API,基于描述符(descriptor-based)
drivers/gpio/gpiolib-legacy.c??##?舊API
include/linux/gpio.h
GPIO子系統(tǒng)有兩套API:
-
- 基于描述符(descriptor-based)
前綴為:
gpiod_
參考:
Documentation/gpio/consumer.txt
-
- 老版本接口(legacy)
前綴為:
gpio_
參考:
Documentation/gpio/gpio-legacy.txt
API對比:
功能 | 新版本接口 | 老版本接口 |
---|---|---|
獲取GPIO | gpiod_get() | gpio_request() |
釋放GPIO | gpiod_put() | gpio_free() |
設(shè)置GPIO輸入 | gpiod_direction_input() | gpio_direction_input() |
設(shè)置GPIO輸出 | gpiod_direction_output() | gpio_direction_output() |
獲取方向 | gpiod_get_direction() | gpio_get_direction( ) |
獲取輸入值 | gpiod_get_value() | gpio_get_value() |
設(shè)置輸出值 | gpiod_set_value() | gpio_set_value() |
2. rk3568 GPIO控制器驅(qū)動
GPIO控制器驅(qū)動實(shí)現(xiàn)文件:
drivers/pinctrl/pinctrl-rockchip.c
gpio涉及主要函數(shù):
?kernel/drivers/gpio/gpio-rockchip.c
三、gpio驅(qū)動編寫實(shí)例
下面以實(shí)際項(xiàng)目中的一個應(yīng)用為例來講解,如何在一個項(xiàng)目中增加一個控制GPIO的邏輯,一看就會。
0. 應(yīng)用場景:
觸摸屏GT1X,觸摸屏的設(shè)備樹和驅(qū)動官方均已提供,硬件信息比如INT、RST、I2C按照實(shí)例填寫即可。
&i2c1?{
?status?=?"okay";
?gt1x:?gt1x@14?{
??compatible?=?"goodix,gt1x";
??reg?=?<0x14>;
??interrupt-parent?=?<&gpio0>;
??interrupts?=?<RK_PD5?IRQ_TYPE_LEVEL_LOW>;
???
??pinctrl-names?=?"default";
??pinctrl-0?=?<&touch_gpio>;
??goodix,rst-gpio?=?<&gpio0?RK_PB6?GPIO_ACTIVE_HIGH>;
??goodix,irq-gpio?=?<&gpio0?RK_PB5?IRQ_TYPE_LEVEL_LOW>;
?};?
};
【pinctrl信息】
&pinctrl?{
…………
?touch?{
??touch_gpio:?touch-gpio?{
???rockchip,pins?=
????<0?RK_PB5?RK_FUNC_GPIO?&pcfg_pull_up>,
????<0?RK_PB6?RK_FUNC_GPIO?&pcfg_pull_none>,
??};
?};
…………
};
但是實(shí)際應(yīng)用中,因?yàn)?a class="article-link" target="_blank" href="/tag/%E7%A1%AC%E4%BB%B6%E8%AE%BE%E8%AE%A1/">硬件設(shè)計需要,有一路點(diǎn)供電引腳AVDD需要由gpio2 A2來提供,
下面我們介紹一下如何再已有的設(shè)備樹、驅(qū)動基礎(chǔ)上添加這個GPIO的功能。
1. 設(shè)備樹
首先我們需要添加該引腳的設(shè)備樹信息,
gt1x:?gt1x@14?{
?……
??goodix,ana-gpio?=?<&gpio2?RK_PA2?GPIO_ACTIVE_HIGH>;
??……
}
&pinctrl?{
…………
?touch?{
??touch_gpio:?touch-gpio?{
???rockchip,pins?=
????<0?RK_PB5?RK_FUNC_GPIO?&pcfg_pull_up>,
????<0?RK_PB6?RK_FUNC_GPIO?&pcfg_pull_none>,
????<2?RK_PA2?RK_FUNC_GPIO?&pcfg_pull_none>;
??};
?};
…………
};
其中添加的設(shè)備樹節(jié)點(diǎn)含義如下:
<2?RK_PA2?RK_FUNC_GPIO?&pcfg_pull_none>;
2 ? RK_PA2 ????:?表示gpio2 PA2,
RK_FUNC_GPIO ??:IOMUX,即要設(shè)置的該引腳的功能
pcfg_pull_none :表示采用默認(rèn)的驅(qū)動強(qiáng)度
RK_PA2、RK_FUNC_GPIO ?定義位于:
[include/dt-bindings/pinctrl/rockchip.h]
#define?RK_GPIO0?0
#define?RK_GPIO1?1
#define?RK_GPIO2?2
#define?RK_GPIO3?3
#define?RK_GPIO4?4
#define?RK_GPIO6?6
#define?RK_PA0??0
#define?RK_PA1??1
#define?RK_PA2??2
#define?RK_PA3??3
#define?RK_PA4??4
……………………
#define?RK_PD6??30
#define?RK_PD7??31
#define?RK_FUNC_GPIO?0
#define?RK_FUNC_0?0
#define?RK_FUNC_1?1
#define?RK_FUNC_2?2
#define?RK_FUNC_3?3
……………………
#define?RK_FUNC_15?15
pcfg_pull_none 定義位于:
[arch/arm64/boot/dts/rockchip/rockchip-pinconf.dtsi]
&pinctrl?{
?……………………
?/omit-if-no-ref/
?pcfg_pull_none:?pcfg-pull-none?{
??bias-disable;
?};
?/omit-if-no-ref/
?pcfg_pull_none_drv_level_0:?pcfg-pull-none-drv-level-0?{
??bias-disable;
??drive-strength?=?<0>;
?};
?/omit-if-no-ref/
?pcfg_pull_none_drv_level_1:?pcfg-pull-none-drv-level-1?{
??bias-disable;
??drive-strength?=?<1>;
?};
?……………………?
};
如果gpio的驅(qū)動強(qiáng)度不夠,可以修改對應(yīng)屬性。
2. 驅(qū)動代碼編寫
我們把所有GPIO操作相關(guān)代碼抽取出來如下:
- 定義
int?gt1x_ana_gpio;
#define?GTP_ANA_PORT?gt1x_ana_gpio
- 注冊
?ret?=?gpio_request(GTP_ANA_PORT,?"GTP_ANA_PORT");
?if?(ret?<?0)?{
??GTP_ERROR("Failed?to?request?GPIO:%d,?ERRNO:%d",?(s32)?GTP_ANA_PORT,?ret);
??return?ret;
?}
- 控制電平
拉高:
gpio_direction_output(GTP_ANA_PORT,?1);
拉低:
gpio_direction_output(GTP_ANA_PORT,?0);???
- 釋放
??if?(gpio_is_valid(gt1x_ana_gpio))
???gpio_free(gt1x_ana_gpio);
3. 添加到觸摸屏GT1X驅(qū)動中
關(guān)于觸摸屏驅(qū)動,一口君后面會寫相應(yīng)的文章來給大家詳細(xì)講解。
下面講解一下,我是如何將GPIO的操作移植到gt1x驅(qū)動中的。
瑞芯微的sdk已經(jīng)包含了觸摸屏驅(qū)動:
drivers/input/touchscreen/gt1x/
├──?gt1x.c
├──?gt1x_cfg.h
├──?gt1x_extents.c
├──?gt1x_firmware.h
├──?gt1x_generic.c
├──?gt1x_generic.h
├──?gt1x.h
├──?gt1x_tools.c
├──?gt1x_update.c
├──?GT5688_Config_20170713_1080_1920.cfg
└──?Makefile
0?directories,?11?files
-
- 增加該GPIO變量定義
[drivers/input/touchscreen/gt1x/gt1x.h]
#define?GTP_ANA_PORT?gt1x_ana_gpio
[drivers/input/touchscreen/gt1x/gt1x.c]
int?gt1x_ana_gpio;
-
- 設(shè)備解析函數(shù)gt1x_parse_dt(),添加解析該引腳的代碼
[drivers/input/touchscreen/gt1x/gt1x.c]
302?static?int?gt1x_parse_dt(struct?device?*dev)?
303?{
??…………
325??gt1x_ana_gpio?=?of_get_named_gpio(np,?"goodix,ana-gpio",?0);
326?+?if?(!gpio_is_valid(gt1x_int_gpio)?||?!gpio_is_valid(gt1x_rst_gpio)?||?!gpio_is_valid(gt1x_ana_gpio))?{
327?+??GTP_ERROR("Invalid?GPIO,?irq-gpio:%d,?rst-gpio:%d,ana_gpio:%d",
328?+???gt1x_int_gpio,?gt1x_rst_gpio,gt1x_ana_gpio);
329???return?-EINVAL;
330??}
331?+????printk("gt1x?gpio?int=%d?rst=%d?ana=%dn",gt1x_int_gpio,gt1x_rst_gpio,gt1x_ana_gpio);
………………
}?
-
- 在函數(shù)gt1x_request_io_port()中增加申請申請該GPIO資源的代碼
400?static?s32?gt1x_request_io_port(void)
401?{
402?????s32?ret?=?0;
…………………………
421?????GTP_GPIO_AS_INPUT(GTP_RST_PORT);
+?ret?=?gpio_request(GTP_ANA_PORT,?"GTP_ANA_PORT");
+?if?(ret?<?0)?{
+??GTP_ERROR("Failed?to?request?GPIO:%d,?ERRNO:%d",?(s32)?GTP_ANA_PORT,?ret);
+??gpio_free(GTP_INT_PORT);
+??gpio_free(GTP_RST_PORT);
+??return?ret;
+?}
422?????return?0;
423?}
-
- 在釋放GPIO和中斷的函數(shù)gt1x_remove_gpio_and_power()釋放該gpio資源
384?static?void?gt1x_remove_gpio_and_power(void)
385?{
386?????if?(gpio_is_valid(gt1x_int_gpio))
387?????????gpio_free(gt1x_int_gpio);
388?
389?????if?(gpio_is_valid(gt1x_rst_gpio))
390?????????gpio_free(gt1x_rst_gpio);
391?
+??if?(gpio_is_valid(gt1x_ana_gpio))
+???gpio_free(gt1x_ana_gpio);
392?????if?(gt1x_i2c_client?&&?gt1x_i2c_client->irq)
393?????????free_irq(gt1x_i2c_client->irq,?gt1x_i2c_client);
394?}
-
- 在什么位置控制該GPIO?
觸摸屏上電時序圖:
由上圖可知,上電的時候,必須首先把AVDD拉高,然后才能繼續(xù)后續(xù)的操作。
之前的驅(qū)動是借用系統(tǒng)的電,但是本例是用GPIO來提供這個電。
觸摸屏驅(qū)動已經(jīng)寫好了相應(yīng)的架構(gòu),AVDD上電/關(guān)閉均封裝到了函數(shù)gt1x_power_switch(),
系統(tǒng)上電初始化會調(diào)用該函數(shù),
s32?gt1x_init(void)
{
?/*?power?on?*/
?gt1x_power_switch(SWITCH_ON);
}
同時當(dāng)屏幕息屏的時候pm子系統(tǒng)會通過對應(yīng)的回調(diào)函數(shù),調(diào)用休眠函數(shù)gt1x_pm_suspend(),喚醒屏幕會調(diào)用gt1x_pm_resume(),
static?const?struct?dev_pm_ops?gt1x_ts_pm_ops?=?{
?.suspend?=?gt1x_pm_suspend,
?.resume?=?gt1x_pm_resume,
};
他們也會在何時的實(shí)際調(diào)用gt1x_power_switch(),
我們只需要在該函數(shù)中加上GPIO拉高、拉低的操作即可。
365?int?gt1x_power_switch(int?on)
366?{
367?????int?ret;
368?????struct?i2c_client?*client?=?gt1x_i2c_client;
369?
370????//?if?(!client?||?!vdd_ana)??注釋掉
371?????//????return?-1;
372?
373?????if?(on)?{
374?????????GTP_DEBUG("GTP?power?on.");
375????????//?ret?=?regulator_enable(vdd_ana);注釋掉
?+??????????GTP_GPIO_OUTPUT(GTP_ANA_PORT,?1);??????
376?????}?else?{
377?????????GTP_DEBUG("GTP?power?off.");
378????????//?ret?=?regulator_disable(vdd_ana);注釋掉
+???????????GTP_GPIO_OUTPUT(GTP_ANA_PORT,?0);
379?????}
380?????return?ret;
381?}
382?#endif???
-
- 此外 ,原有的供電代碼都需要刪除。
298?//static?struct?regulator?*vdd_ana;?
331?#if?0
332?????vdd_ana?=?devm_regulator_get_optional(dev,?"vdd_ana");
333?????if?(PTR_ERR(vdd_ana)?==?-ENODEV)?{
334?????????GTP_ERROR("vdd_ana?not?specified,?fallback?to?power-supply");
335?????????vdd_ana?=?devm_regulator_get_optional(dev,?"power");
336?????????if?(PTR_ERR(vdd_ana)?==?-ENODEV)?{
337?????????????GTP_ERROR("power?not?specified,?ignore?power?ctrl");
338?????????????vdd_ana?=?NULL;
339?????????}
340?????}
341?????if?(IS_ERR(vdd_ana))?{
342?????????GTP_ERROR("regulator?get?of?vdd_ana/power-supply?failed");????????????????????????????????
343?????????return?PTR_ERR(vdd_ana);
344?????}
345?#endif
四、 查看已經(jīng)申請了的GPIO
系統(tǒng)啟動后,可以通過debugfs查看GPIO分配情況
rk3568_r:/sys/kernel/debug?#?cat?gpio???????????????????????????????????????????????
cat?gpio????????????????????????????????????????????????????????????????????????????
gpiochip0:?GPIOs?0-31,?parent:?platform/fdd60000.gpio,?gpio0:???????????????????????
?gpio-5???(????????????????????|vcc5v0_otg??????????)?out?lo????????????????????????
?gpio-6???(????????????????????|vcc5v0_host?????????)?out?hi????????????????????????
?gpio-13??(????????????????????|GTP_INT_IRQ?????????)?in??hi????????????????????????
?gpio-14??(????????????????????|GTP_RST_PORT????????)?out?hi????????????????????????
?gpio-16??(????????????????????|work????????????????)?out?lo????????????????????????
?gpio-17??(????????????????????|vcc_camera??????????)?out?hi????????????????????????
?gpio-18??(????????????????????|hpd?????????????????)?in??lo????????????????????????
……………………
gpiochip1:?GPIOs?32-63,?parent:?platform/fe740000.gpio,?gpio1:??????????????????????
????????????????????????????????????????????????????????????????????????????????????
gpiochip2:?GPIOs?64-95,?parent:?platform/fe750000.gpio,?gpio2:??????????????????????
?gpio-66??(????????????????????|GTP_ANA_PORT????????)?out?lo????????????????????????
?gpio-73??(????????????????????|bt_default_rts??????)?in??hi????????????????????????
?gpio-89??(????????????????????|mdio-reset??????????)?out?hi????????????????????????
?gpio-91??(????????????????????|mdio-reset??????????)?out?hi????????????????????????
?gpio-94??(????????????????????|reset???????????????)?out?lo??????????????????????????????????????????????????????????????????????????????????????????
……………………
可以看到設(shè)備樹中的3個GPIO信息:
?gpio-13??(????????????????????|GTP_INT_IRQ?????????)?in??hi????????????????????????
?gpio-14??(????????????????????|GTP_RST_PORT????????)?out?hi??
?gpio-66??(????????????????????|GTP_ANA_PORT????????)?out?lo????????????
如果debugfs沒有掛在,使用下面命令掛載
mount?-t?debugfs?none?/sys/kernel/debug
五、總結(jié)
實(shí)際上,GPIO編寫還是很簡單的,驅(qū)動不論多復(fù)雜,最終都還是由這些基本的函數(shù)來實(shí)現(xiàn)的,
這是因?yàn)樵陂_發(fā)一個新的產(chǎn)品的時候,sdk中很多硬件的配置信息,往往是廠家自己出廠的一個demo板子的硬件信息,
而很多GPIO功能的配置可能和我們實(shí)際需求有差異,
經(jīng)常出現(xiàn)某個設(shè)備的GPIO配置了,但是工作卻不正常的情況發(fā)生,
原因就是iomux并不是自己所需要的功能,
下一篇,給大家詳細(xì)講解,如何定位GPIO復(fù)用的問題。