rk3568 | rk平台GPIO冲突检测小技巧
来源:一口Linux发布时间:2022-12-263096浏览
询问 AI上一篇我们讲解了如何编写gpio驱动,但是实际操作中,经常发现gpio引脚被占用的情况发生,那么本篇文章就详细讲解rxw平台下如何快速定位gpio复用问题以及如何解决。
要想查看某个GPIO引脚可以配置的功能以及地址信息,需要查看TRM手册:
《RockchipRK3568TRMPart1》
- 第一步:对于GPIO2 A2,我们转换成下面字符串然后搜索
gpio2a2_sel
这样我们就可以直接找到该引脚iomux配置寄存器,bit[10:8]。该寄存器地址:
基地址+0x0020
那么如何找到基地址呢?
- 第二步:
搜索该引脚寄存器的名字:
GRF_GPIO2A_IOMUX_L
注意向上搜索
可以得到该寄存器基地址名称:
SYS_GRF
- 第三步直接进入chapter 3
可得SYS_GRF 基地址
0xFDC60000
那么我们就得到了GPIO2 A2的IOMUX配置寄存器地址为
0xFDC60000+0x0020
该寄存器的bit[10:8]用于设置该寄存器功能。
当然也可以用下图来查找,更加方便:
那么找到了这个寄存器地址,我要如何来直接读取这个寄存器的值呢?
这个可以借助于一个命令io。
io命令可以直接操作某个寄存器,用于查看设置某个PIO 引脚配置了什么iomux,非常方便。
RK 的 Android 平台,默认有包含 io 工具(源码位置:external\io), linux 系统平台如果没有此源码, 可以将 Android 平台此源码打包过去编译即可( linux 平台代码同步最新都已带有 IO 工具,可直接使用命令)。
- 第一步:修改Makefile、Kconfig
vimdrivers/char/Makefile
增加
+obj-$(CONFIG_DEVMEM)+=mem.o
vimdrivers/char/Kconfig
改文件已经包含下面信息
10configDEVMEM
11bool"/dev/memvirtualdevicesupport"
12defaulty
13help
14SayYhereifyouwanttosupportthe/dev/memdevice.
15The/dev/memdeviceisusedtoaccessareasofphysical
16memory.
17Whenindoubt,say"Y".
- 第二步:修改驱动文件
io命令需要内核驱动支持,驱动文件如下:
drivers/char/mem.c
找到下面代码:
#ifdefCONFIG_DEVMEM
[1]={"mem",0,mem_fops,FMODE_UNSIGNED_OFFSET},
#endif
修改为
+//#ifdefCONFIG_DEVMEM
[1]={"mem",0,mem_fops,FMODE_UNSIGNED_OFFSET},
+//#endif
- 第三步:修改配置文件rockchip_defconfig
vimarch/arm64/configs/rockchip_defconfig
增加
+CONFIG_DEVMEM=y
重新编译内核,烧录重启。
rockchip_defconfig需要根据平台选择
- 第四步:查看mem字符设备:
rk3568_r:/#ls/dev/mem-l
ls/dev/mem-l
crw-------1mediamedia1,12017-08-0409:00/dev/mem
io命令正是通过这个字符设备来实现寄存器的读写的。
rk3568_r:/dev#io
io
Rawmemoryi/outility-$Revision:1.5$
io-v-1|2|4-r|w[-l<len>][-f<file>]<addr>[<value>]
-vVerbose,asksforconfirmation
-1|2|4Setsmemoryaccesssizeinbytes(defaultbyte)
-l<len>Lengthinbytesofareatoaccess(defaultsto
oneaccess,orwholefilelength)
-r|wReadfromorWritetomemory(defaultread)
-f<file>Filetowriteonmemoryread,or
toreadonmemorywrite
<addr>Thememoryaddresstoaccess
<val>Thevaluetowrite(implies-w)
Examples:
io0x1000Readsonebytefrom0x1000
io0x10000x12Writes0x12tolocation0x1000
io-2-l80x1000Reads8wordsfrom0x1000
io-r-fdmp-l100200Reads100bytesfromaddr200tofile
io-w-fimg0x10000Writesthewholeoffiletomemory
Noteaccesssize(-1|2|4)doesnotapplytofilebasedaccesses.
读取gpio2 A2引脚配置寄存器
io-4-l0x300xFDC60000
-4:按字(4个字节)访问内存
0x30:读取0x30(48)个字节
0xFDC60000:基地址

由上图可得0xFDC60020的bit[10:8]值为1
所以该io引脚被设置的iomux为
3'h1:SDMMC0_CLK
如果我们是希望通过设备树设置该引脚为普通gpio,那么该值应该为0,
那么这就说明了,我们设置失败了,
那就可以到设备树中查找设置SDMMC0的地方,将其注释掉。
要读取单个寄存器,可以用下面命令:
io-4-r0xFDC60024
比如已经通过命令:io -4 -r 0xFDC60024读出了寄存器的值,那么此时想把gpio2 A2修改为普通GPIO,那么只需要对0xFDC60024这个寄存器的第 3 个 1 写 0,那么可以如下操作:
io-4–w0xFDC600240x01002011
注意:
- 第三个1对应是bit[10:8]
- 通过 io 写的寄存器值 reboot 后不会保留
- 为什么寄存器地址后面的十六进制值的 bit [24]写 1?因为该寄存器的 16bit 至31 bit 是写有效位,默认为 0,即不可写。因为要往[8] bit 写 1,所以[8] bit 对应的写有效位 16 bit 也要对应置 1 才可写入,这个是根据寄存器描述而定的。
修改过后,再查看,可以发现对应的位的值变为了1:
思 路 :驱 动 里 如 果 想 要 去 操 作 GPIO , 肯 定 会 调 用 到 gpio_direction_output 、gpio_direction_input、 gpiod_direction_output、 gpiod_direction_input 这几个接口, 他们的定义位置是:
vimkernel/drivers/gpio/gpiolib.c
在这些接口里添加判断对应查询的 IO 口条件,通过以下函数就可以打印出哪些模块复用了该引脚:
dump_stack();
注意:在 linux3.10 内核的 sdk 中用的是 gpio_direction_output 和gpio_direction_input 接 口 ,在 linux4.4 内 核 中 则 是 gpiod_direction_output 和gpiod_direction_input。
下面以 linux4.4 版本为例来讲解如何查看引脚:gpio4 b3。
首先,需要计算出代表 gpio4b3 的值,算法如下:
gpio4_B3=4*32+(B-A)*8+3=3*32+1*8+3=139
计算方法参考:《rk3568 | 瑞芯微平台GPIO引脚驱动编写》
- 最前面和 32 相乘的数字因为是 gpio4,所以是 432。如果是 gpio3,那就是 332;
- 括号里面的 A、 B、 C、 D 分别代表数值 0、 1、 2、 3,在计算时候分别对应去减即可,这里因为是 B3,所以用 B-A,如果是 C3,就是 C-A;
- 最后的+3 是因为是 B3,如果是 GPIO4B2,那么最后就+2。
注:在 linux4.4 内核, io 引脚的值有些变化,也就是按照上文算法计算的结果要+1000,所以 GPIO4B3 如果是 linux4.4 内核里要填 1139。
intgpiod_direction_output(structgpio_desc*desc,intvalue)
{
if(!desc||!desc->chip){
pr_warn("%s:invalidGPIO\n",__func__);
return-EINVAL;
}
+if(desc_to_gpio(desc)==1139)
+{
+printk("dump_stack_start\n");
+dump_stack();
+printk("dump_stack_end\n");
+}
if(test_bit(FLAG_ACTIVE_LOW,desc->flags))
value=!value;
return_gpiod_direction_output_raw(desc,value);
}
添加后编译烧录,只要对应判断的引脚有被调用,启动 log 中就会打印出堆栈,可以根据找出结果查看,找到驱动调用函数。
配置IOMUX会调用该接口,
仍然以引脚gpio4 b3为例。
[drivers/pinctrl/pinctrl-rockchip.c]
@@-1224,6+1224,17@@staticintrockchip_set_mux(structrockchip_pin_bank*bank,intpin,intmux)
dev_dbg(info->dev,"settingmuxofGPIO%d-%dto%d\n",
bank->bank_num,pin,mux);
+if((bank->bank_num==4)(pin==11)){
+printk("6902settingmuxofGPIO%d-%dto%d\n",
+bank->bank_num,pin,mux);
+dump_stack();
+}
if(bank->iomux[iomux_num].typeIOMUX_SOURCE_PMU)
regmap=info->regmap_pmu;
- bank_num 表示gpio4
- 这里“ pin ==”后面跟的值计算方式为:将 A0 至 D7 32 个引脚顺序对应数值 0 至 31,b3为11。
刚才分析,发现GPIO2 A2被SDMMC0占用,那么如何来解决这个冲突呢?
只要从设备树下手即可。
瑞芯微平台的设备树,根据平台区分,往往前缀是:
rk+平台+板子型号+ddr型号+版本
比如rk3568系列设计的设备树文件如下:
arch/arm64/boot/dts/rockchip/rk3568-amp.dtsi
arch/arm64/boot/dts/rockchip/rk3568-android9.dtsi
arch/arm64/boot/dts/rockchip/rk3568-android.dtsi
arch/arm64/boot/dts/rockchip/rk3568-dram-default-timing.dtsi
arch/arm64/boot/dts/rockchip/rk3568.dtsi
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10-android9.dts
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtb
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10-linux.dts
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10-linux-spi-nor.dts
arch/arm64/boot/dts/rockchip/rk3568-evb2-lp4x-v10-bt1120-to-hdmi.dts
arch/arm64/boot/dts/rockchip/rk3568-evb2-lp4x-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-evb2-lp4x-v10.dtsi
arch/arm64/boot/dts/rockchip/rk3568-evb4-lp3-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-evb5-ddr4-v10.dtsi
arch/arm64/boot/dts/rockchip/rk3568-evb6-ddr3-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-evb6-ddr3-v10.dtsi
arch/arm64/boot/dts/rockchip/rk3568-evb6-ddr3-v10-linux.dts
arch/arm64/boot/dts/rockchip/rk3568-evb6-ddr3-v10-rk628-bt1120-to-hdmi.dts
arch/arm64/boot/dts/rockchip/rk3568-evb6-ddr3-v10-rk628-rgb2hdmi.dts
arch/arm64/boot/dts/rockchip/rk3568-evb6-ddr3-v10-rk630-bt656-to-cvbs.dts
arch/arm64/boot/dts/rockchip/rk3568-evb7-ddr4-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-evb.dtsi
arch/arm64/boot/dts/rockchip/rk3568-iotest-ddr3-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-iotest-ddr3-v10-linux.dts
arch/arm64/boot/dts/rockchip/rk3568-linux.dtsi
arch/arm64/boot/dts/rockchip/rk3568-nvr-demo-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-nvr-demo-v10.dtsi
arch/arm64/boot/dts/rockchip/rk3568-nvr-demo-v10-linux.dts
arch/arm64/boot/dts/rockchip/rk3568-nvr-demo-v10-linux-spi-nand.dts
arch/arm64/boot/dts/rockchip/rk3568-nvr-demo-v12.dtsi
arch/arm64/boot/dts/rockchip/rk3568-nvr-demo-v12-linux.dts
arch/arm64/boot/dts/rockchip/rk3568-nvr-demo-v12-linux-spi-nand.dts
arch/arm64/boot/dts/rockchip/rk3568-nvr.dtsi
arch/arm64/boot/dts/rockchip/rk3568-nvr-linux.dtsi
arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi
一口君的板子是evb1,ddr4,v10版本,所以去掉其他的文件,
我们只需要关注以下文件即可。
arch/arm64/boot/dts/rockchip/rk3568-android.dtsi
与安卓相关的信息
arch/arm64/boot/dts/rockchip/rk3568.dtsi
描述cpu、memory、timer、clk、sata、usbhost、gic、视频控制器、sram、cru、i2c控制器、uart、pwm、pmu等各种Soc内部硬件信息
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dts
arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi
arch/arm64/boot/dts/rockchip/rk3568-evb.dtsi
与evb1底板相关的外设硬件信息
arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi
pinctl相关硬件信息
还有1个描述pinctl引脚驱动能力的文件:
rockchip-pinconf.dtsi
使用grep命令来查询:
grepsdmmc0arch/arm64/boot/dts/rockchip/rk3568*-nr

好在信息不多,逐个查看,下面这个文件,我们找到了复用的地方。
rk3568-pinctrl.dtsi
该引脚被定义为sdmmc0_clk,作为时钟被使用了。
sdmmc0{
………………
/omit-if-no-ref/
sdmmc0_clk:sdmmc0-clk{
rockchip,pins=
/*sdmmc0_clk*/
<2RK_PA21pcfg_pull_up_drv_level_2>;
};
修改的方法有很多种:
投机取巧法将sdmmc0_clk改成其他没有用的gpio
简单粗暴法如果确定没有使用sdmmc0,可以将所有sdmmc0地方全部注释掉
设备树支持下面这种方法:
#if0
#endif
- 硬件飞线法
找硬件工程师飞线,改用其他的GPIO
- 最优法如果sdmmc0也用到了,
那就只能修改冲突的GPIO,
但是这种情况,往往会牵一发而动全身,
要改好几处,那就需要各位老铁细心慢慢修改了。
好了,本文到底结束。
一口君目标是写100篇瑞芯微平台的文章,
有喜欢瑞芯微的老铁,
欢迎大家关注学习。
一口Linux
新闻来源:一口Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
