rk3568 | 瑞芯微平台GPIO引脚驱动编写
来源:一口Linux发布时间:2022-12-201908浏览
询问 AI今天的文章稍微晚了一点,因为一口君阳了。
烧的很难受,昨天晚上和今天下午各吃了1颗布洛芬,现在好一些了,希望大家都晚一点阳,症状都轻一点。
所有瑞芯微的文章合集:
《瑞芯微文章合集》
最近在玩瑞芯微平台的产品,移植了几个设备的驱动,遇到了一些问题,总结后发现大部分问题都出在了GPIO配置的问题上,写下本篇文章,用来分享一下调试的心得。
有喜欢瑞芯微的朋友,可以加我好友,拉你进群,一起学习进步。
本文基于rk3568平台。
要查看rk3568 GPIO分组及地址信息,需要查看TRM手册,下载地址:
http://opensource.rock-chips.com

也可以公-众-号后台回复:rxw可获取更丰富的资料。
GPIO(General Purpose Input/Output Port):通用输入输出端口。
除作为一般的输入/输出功能外,还可以配置为中断和模拟UART、CAN、PWM、I2C、SDMMC、CLK等功能。
一共有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,usedforalwaysonlogiccontrol
CPU_GRF,usedforalwaysonsystem
DDR_GRF,usedforDDRsystem
PIPE_GRF,usedforpipeinterfacecontroller,
SYS_GRF,usedforgeneralsystem
PIPEPHY_GRF,usedforpipeinterfacephy
USBPHY_U3_GRF,usedforusb3phy
USBPHY_U2_GRF,usedforusb2phy
EDP_PHY_GRF,usedforeDPPHYcontrol
PCIEPHY_GRF,usedforpcie3.0phy
USB_GRF,usedforusb2hostcontroller
要查找GPIO对应的配置寄存器地址,必须知道他属于哪个分组:
pins=32*bank_num+8*group+x
bank_num:0~4,对应GPIO0~4
group:0~3,对应GPIOA~D
例如GPIO2 A2:
GPIO2_A2=32*2+8*0+2=66
在这里插入图片描述由上图可得:
- gpio0 bank属于PMU分组, 基地址:0xFDC20000
- gpio0~4 bank 属于SYS分组,基地址:0xFDC60000
- GPIO0 bank控制pin0~31
- GPIO1 bank控制pin32~63
- GPIO2 bank控制pin64~95
- GPIO3 bank控制pin96~127
- GPIO4 bank控制pin128~159
通过上图,很方便查找到对应的GPIO引脚号以及IOMUX control寄存器地址。一口君还画了下面这个图,大家根据自己喜好,看看哪一个比较好:

也可以用debugfs来查看pin与gpio号之间映射关系
rk3568_r:/sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl#catpins
catpins
registeredpins:160
##GPIO0_*包括的GPIO
pin0(gpio0-0)
pin1(gpio0-1)
pin2(gpio0-2)
pin3(gpio0-3)
pin4(gpio0-4)
pin5(gpio0-5)
pin6(gpio0-6)
pin7(gpio0-7)
pin8(gpio0-8)
……
##GPIO1_*包括的GPIO
pin32(gpio1-0)
pin33(gpio1-1)
pin34(gpio1-2)
pin35(gpio1-3)
pin36(gpio1-4)
pin37(gpio1-5)
pin38(gpio1-6)
pin39(gpio1-7)
pin40(gpio1-8)
……
##GPIO2_*包括的GPIO
pin64(gpio2-0)
pin65(gpio2-1)
pin66(gpio2-2)
pin67(gpio2-3)
pin68(gpio2-4)
pin69(gpio2-5)
pin70(gpio2-6)
pin71(gpio2-7)
pin72(gpio2-8)
……
##GPIO3_*包括的GPIO
pin96(gpio3-0)
pin97(gpio3-1)
pin98(gpio3-2)
pin99(gpio3-3)
pin100(gpio3-4)
pin101(gpio3-5)
pin102(gpio3-6)
pin103(gpio3-7)
pin104(gpio3-8)
……
##GPIO2_*包括的GPIO
pin128(gpio4-0)
pin129(gpio4-1)
pin130(gpio4-2)
pin131(gpio4-3)
pin132(gpio4-4)
pin133(gpio4-5)
pin134(gpio4-6)
pin135(gpio4-7)
pin136(gpio4-8)
……
pin159(gpio4-31)
Linux内核GPIO主要实现文件:
drivers/gpio/gpiolib.c##新版API,基于描述符(descriptor-based)
drivers/gpio/gpiolib-legacy.c##旧API
include/linux/gpio.h
GPIO子系统有两套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() |
| 设置GPIO输入 | gpiod_direction_input() | gpio_direction_input() |
| 设置GPIO输出 | gpiod_direction_output() | gpio_direction_output() |
| 获取方向 | gpiod_get_direction() | gpio_get_direction( ) |
| 获取输入值 | gpiod_get_value() | gpio_get_value() |
| 设置输出值 | gpiod_set_value() | gpio_set_value() |
GPIO控制器驱动实现文件:
drivers/pinctrl/pinctrl-rockchip.c
gpio涉及主要函数:
kernel/drivers/gpio/gpio-rockchip.c

下面以实际项目中的一个应用为例来讲解,如何在一个项目中增加一个控制GPIO的逻辑,一看就会。
触摸屏GT1X,触摸屏的设备树和驱动官方均已提供,硬件信息比如INT、RST、I2C按照实例填写即可。
i2c1{
status="okay";
gt1x:gt1x@14{
compatible="goodix,gt1x";
reg=<0x14>;
interrupt-parent=<gpio0>;
interrupts=<RK_PD5IRQ_TYPE_LEVEL_LOW>;
pinctrl-names="default";
pinctrl-0=<touch_gpio>;
goodix,rst-gpio=<gpio0RK_PB6GPIO_ACTIVE_HIGH>;
goodix,irq-gpio=<gpio0RK_PB5IRQ_TYPE_LEVEL_LOW>;
};
};
【pinctrl信息】
pinctrl{
…………
touch{
touch_gpio:touch-gpio{
rockchip,pins=
<0RK_PB5RK_FUNC_GPIOpcfg_pull_up>,
<0RK_PB6RK_FUNC_GPIOpcfg_pull_none>,
};
};
…………
};
但是实际应用中,因为硬件设计需要,有一路点供电引脚AVDD需要由gpio2 A2来提供,
下面我们介绍一下如何再已有的设备树、驱动基础上添加这个GPIO的功能。
首先我们需要添加该引脚的设备树信息,
gt1x:gt1x@14{
……
goodix,ana-gpio=<gpio2RK_PA2GPIO_ACTIVE_HIGH>;
……
}
pinctrl{
…………
touch{
touch_gpio:touch-gpio{
rockchip,pins=
<0RK_PB5RK_FUNC_GPIOpcfg_pull_up>,
<0RK_PB6RK_FUNC_GPIOpcfg_pull_none>,
<2RK_PA2RK_FUNC_GPIOpcfg_pull_none>;
};
};
…………
};
其中添加的设备树节点含义如下:
<2RK_PA2RK_FUNC_GPIOpcfg_pull_none>;
2 RK_PA2 :表示gpio2 PA2,
RK_FUNC_GPIO :IOMUX,即要设置的该引脚的功能
pcfg_pull_none :表示采用默认的驱动强度
RK_PA2、RK_FUNC_GPIO 定义位于:
[include/dt-bindings/pinctrl/rockchip.h]
#defineRK_GPIO00
#defineRK_GPIO11
#defineRK_GPIO22
#defineRK_GPIO33
#defineRK_GPIO44
#defineRK_GPIO66
#defineRK_PA00
#defineRK_PA11
#defineRK_PA22
#defineRK_PA33
#defineRK_PA44
……………………
#defineRK_PD630
#defineRK_PD731
#defineRK_FUNC_GPIO0
#defineRK_FUNC_00
#defineRK_FUNC_11
#defineRK_FUNC_22
#defineRK_FUNC_33
……………………
#defineRK_FUNC_1515
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的驱动强度不够,可以修改对应属性。
我们把所有GPIO操作相关代码抽取出来如下:
- 定义
intgt1x_ana_gpio;
#defineGTP_ANA_PORTgt1x_ana_gpio
- 注册
ret=gpio_request(GTP_ANA_PORT,"GTP_ANA_PORT");
if(ret<0){
GTP_ERROR("FailedtorequestGPIO:%d,ERRNO:%d",(s32)GTP_ANA_PORT,ret);
returnret;
}
- 控制电平
拉高:
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);
关于触摸屏驱动,一口君后面会写相应的文章来给大家详细讲解。
下面讲解一下,我是如何将GPIO的操作移植到gt1x驱动中的。
瑞芯微的sdk已经包含了触摸屏驱动:
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
0directories,11files
- 增加该GPIO变量定义
[drivers/input/touchscreen/gt1x/gt1x.h]
#defineGTP_ANA_PORTgt1x_ana_gpio
[drivers/input/touchscreen/gt1x/gt1x.c]
intgt1x_ana_gpio;
- 设备解析函数gt1x_parse_dt(),添加解析该引脚的代码
[drivers/input/touchscreen/gt1x/gt1x.c]
302staticintgt1x_parse_dt(structdevice*dev)
303{
…………
325gt1x_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("InvalidGPIO,irq-gpio:%d,rst-gpio:%d,ana_gpio:%d",
328+gt1x_int_gpio,gt1x_rst_gpio,gt1x_ana_gpio);
329return-EINVAL;
330}
331+printk("gt1xgpioint=%drst=%dana=%d\n",gt1x_int_gpio,gt1x_rst_gpio,gt1x_ana_gpio);
………………
}
- 在函数gt1x_request_io_port()中增加申请申请该GPIO资源的代码
400statics32gt1x_request_io_port(void)
401{
402s32ret=0;
…………………………
421GTP_GPIO_AS_INPUT(GTP_RST_PORT);
+ret=gpio_request(GTP_ANA_PORT,"GTP_ANA_PORT");
+if(ret<0){
+GTP_ERROR("FailedtorequestGPIO:%d,ERRNO:%d",(s32)GTP_ANA_PORT,ret);
+gpio_free(GTP_INT_PORT);
+gpio_free(GTP_RST_PORT);
+returnret;
+}
422return0;
423}
- 在释放GPIO和中断的函数gt1x_remove_gpio_and_power()释放该gpio资源
384staticvoidgt1x_remove_gpio_and_power(void)
385{
386if(gpio_is_valid(gt1x_int_gpio))
387gpio_free(gt1x_int_gpio);
388
389if(gpio_is_valid(gt1x_rst_gpio))
390gpio_free(gt1x_rst_gpio);
391
+if(gpio_is_valid(gt1x_ana_gpio))
+gpio_free(gt1x_ana_gpio);
392if(gt1x_i2c_clientgt1x_i2c_client->irq)
393free_irq(gt1x_i2c_client->irq,gt1x_i2c_client);
394}
- 在什么位置控制该GPIO?
触摸屏上电时序图:
由上图可知,上电的时候,必须首先把AVDD拉高,然后才能继续后续的操作。
之前的驱动是借用系统的电,但是本例是用GPIO来提供这个电。
触摸屏驱动已经写好了相应的架构,AVDD上电/关闭均封装到了函数gt1x_power_switch(),
系统上电初始化会调用该函数,
s32gt1x_init(void)
{
/*poweron*/
gt1x_power_switch(SWITCH_ON);
}
同时当屏幕息屏的时候pm子系统会通过对应的回调函数,调用休眠函数gt1x_pm_suspend(),唤醒屏幕会调用gt1x_pm_resume(),
staticconststructdev_pm_opsgt1x_ts_pm_ops={
.suspend=gt1x_pm_suspend,
.resume=gt1x_pm_resume,
};
他们也会在何时的实际调用gt1x_power_switch(),
我们只需要在该函数中加上GPIO拉高、拉低的操作即可。
365intgt1x_power_switch(inton)
366{
367intret;
368structi2c_client*client=gt1x_i2c_client;
369
370//if(!client||!vdd_ana)注释掉
371//return-1;
372
373if(on){
374GTP_DEBUG("GTPpoweron.");
375//ret=regulator_enable(vdd_ana);注释掉
+GTP_GPIO_OUTPUT(GTP_ANA_PORT,1);
376}else{
377GTP_DEBUG("GTPpoweroff.");
378//ret=regulator_disable(vdd_ana);注释掉
+GTP_GPIO_OUTPUT(GTP_ANA_PORT,0);
379}
380returnret;
381}
382#endif
- 此外 ,原有的供电代码都需要删除。
298//staticstructregulator*vdd_ana;
331#if0
332vdd_ana=devm_regulator_get_optional(dev,"vdd_ana");
333if(PTR_ERR(vdd_ana)==-ENODEV){
334GTP_ERROR("vdd_ananotspecified,fallbacktopower-supply");
335vdd_ana=devm_regulator_get_optional(dev,"power");
336if(PTR_ERR(vdd_ana)==-ENODEV){
337GTP_ERROR("powernotspecified,ignorepowerctrl");
338vdd_ana=NULL;
339}
340}
341if(IS_ERR(vdd_ana)){
342GTP_ERROR("regulatorgetofvdd_ana/power-supplyfailed");
343returnPTR_ERR(vdd_ana);
344}
345#endif
系统启动后,可以通过debugfs查看GPIO分配情况
rk3568_r:/sys/kernel/debug#catgpio
catgpio
gpiochip0:GPIOs0-31,parent:platform/fdd60000.gpio,gpio0:
gpio-5(|vcc5v0_otg)outlo
gpio-6(|vcc5v0_host)outhi
gpio-13(|GTP_INT_IRQ)inhi
gpio-14(|GTP_RST_PORT)outhi
gpio-16(|work)outlo
gpio-17(|vcc_camera)outhi
gpio-18(|hpd)inlo
……………………
gpiochip1:GPIOs32-63,parent:platform/fe740000.gpio,gpio1:
gpiochip2:GPIOs64-95,parent:platform/fe750000.gpio,gpio2:
gpio-66(|GTP_ANA_PORT)outlo
gpio-73(|bt_default_rts)inhi
gpio-89(|mdio-reset)outhi
gpio-91(|mdio-reset)outhi
gpio-94(|reset)outlo
……………………
可以看到设备树中的3个GPIO信息:
gpio-13(|GTP_INT_IRQ)inhi
gpio-14(|GTP_RST_PORT)outhi
gpio-66(|GTP_ANA_PORT)outlo
如果debugfs没有挂在,使用下面命令挂载
mount-tdebugfsnone/sys/kernel/debug
实际上,GPIO编写还是很简单的,驱动不论多复杂,最终都还是由这些基本的函数来实现的,
这是因为在开发一个新的产品的时候,sdk中很多硬件的配置信息,往往是厂家自己出厂的一个demo板子的硬件信息,
而很多GPIO功能的配置可能和我们实际需求有差异,
经常出现某个设备的GPIO配置了,但是工作却不正常的情况发生,
烟瘾就是iomux并不是自己所需要的功能,
下一篇,给大家详细讲解,如何定位GPIO复用的问题。
新闻来源:一口Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
