上一篇我們講解了如何編寫gpio驅(qū)動,但是實際操作中,經(jīng)常發(fā)現(xiàn)gpio引腳被占用的情況發(fā)生,那么本篇文章就詳細講解rxw平臺下如何快速定位gpio復(fù)用問題以及如何解決。
一、GPIO寄存器查找
要想查看某個GPIO引腳可以配置的功能以及地址信息,需要查看TRM手冊:
《Rockchip?RK3568?TRM?Part1》
- 第一步:對于GPIO2 A2,我們轉(zhuǎn)換成下面字符串然后搜索
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]用于設(shè)置該寄存器功能。
當(dāng)然也可以用下圖來查找,更加方便:
那么找到了這個寄存器地址,我要如何來直接讀取這個寄存器的值呢?
這個可以借助于一個命令io。
二、io命令
io命令可以直接操作某個寄存器,用于查看設(shè)置某個PIO 引腳配置了什么iomux,非常方便。
1. 移植io命令需要的驅(qū)動
RK 的 Android 平臺,默認(rèn)有包含 io 工具(源碼位置:externalio), linux 系統(tǒng)平臺如果沒有此源碼, 可以將 Android 平臺此源碼打包過去編譯即可( linux 平臺代碼同步最新都已帶有 IO 工具,可直接使用命令)。
- 第一步:修改Makefile、Kconfig
vim??drivers/char/Makefile
增加
+obj-$(CONFIG_DEVMEM)???????????+=?mem.o
vim?drivers/char/Kconfig
改文件已經(jīng)包含下面信息
?10?config?DEVMEM
?11?????bool?"/dev/mem?virtual?device?support"
?12?????default?y
?13?????help
?14???????Say?Y?here?if?you?want?to?support?the?/dev/mem?device.
?15???????The?/dev/mem?device?is?used?to?access?areas?of?physical
?16???????memory.
?17???????When?in?doubt,?say?"Y".
- 第二步:修改驅(qū)動文件
io命令需要內(nèi)核驅(qū)動支持,驅(qū)動文件如下:
drivers/char/mem.c
找到下面代碼:
#ifdef?CONFIG_DEVMEM
?????????[1]?=?{?"mem",?0,?&mem_fops,?FMODE_UNSIGNED_OFFSET?},
#endif
修改為
+//#ifdef?CONFIG_DEVMEM
?????????[1]?=?{?"mem",?0,?&mem_fops,?FMODE_UNSIGNED_OFFSET?},
+//#endif
- 第三步:修改配置文件rockchip_defconfig
vim??arch/arm64/configs/rockchip_defconfig
增加
+CONFIG_DEVMEM=y
重新編譯內(nèi)核,燒錄重啟。
rockchip_defconfig需要根據(jù)平臺選擇
- 第四步:查看mem字符設(shè)備:
rk3568_r:/?#?ls?/dev/mem?-l
ls?/dev/mem?-l
crw-------?1?media?media?1,???1?2017-08-04?09:00?/dev/mem
io命令正是通過這個字符設(shè)備來實現(xiàn)寄存器的讀寫的。
2. 命令說明
rk3568_r:/dev?#?io
io
Raw?memory?i/o?utility?-?$Revision:?1.5?$
io?-v?-1|2|4?-r|w?[-l?<len>]?[-f?<file>]?<addr>?[<value>]
????-v?????????Verbose,?asks?for?confirmation
????-1|2|4?????Sets?memory?access?size?in?bytes?(default?byte)
????-l?<len>???Length?in?bytes?of?area?to?access?(defaults?to
???????????????one?access,?or?whole?file?length)
????-r|w???????Read?from?or?Write?to?memory?(default?read)
????-f?<file>??File?to?write?on?memory?read,?or
???????????????to?read?on?memory?write
????<addr>?????The?memory?address?to?access
????<val>??????The?value?to?write?(implies?-w)
Examples:
????io?0x1000??????????????????Reads?one?byte?from?0x1000
????io?0x1000?0x12?????????????Writes?0x12?to?location?0x1000
????io?-2?-l?8?0x1000??????????Reads?8?words?from?0x1000
????io?-r?-f?dmp?-l?100?200????Reads?100?bytes?from?addr?200?to?file
????io?-w?-f?img?0x10000???????Writes?the?whole?of?file?to?memory
Note?access?size?(-1|2|4)?does?not?apply?to?file?based?accesses.
3. 舉例1,通過IO讀寄存器:
讀取gpio2 A2引腳配置寄存器
io?-4?-l?0x30?0xFDC60000
-4?????????:?按字(4個字節(jié))?訪問內(nèi)存?
0x30???????:?讀取0x30(48)個字節(jié)
0xFDC60000?:?基地址
由上圖可得0xFDC60020的bit[10:8]值為1
所以該io引腳被設(shè)置的iomux為
3'h1:?SDMMC0_CLK
如果我們是希望通過設(shè)備樹設(shè)置該引腳為普通gpio,那么該值應(yīng)該為0,
那么這就說明了,我們設(shè)置失敗了,
那就可以到設(shè)備樹中查找設(shè)置SDMMC0的地方,將其注釋掉。
要讀取單個寄存器,可以用下面命令:
?io?-4?-r?0xFDC60024
4. 舉例2,通過IO寫寄存器
比如已經(jīng)通過命令:io -4 -r 0xFDC60024讀出了寄存器的值,那么此時想把gpio2 A2修改為普通GPIO,
那么只需要對0xFDC60024這個寄存器的第 3 個 1 寫 0,那么可以如下操作:
io?-4?–w?0xFDC60024?0x01002011
注意:
-
- 第三個1對應(yīng)是bit[10:8]通過 io 寫的寄存器值 reboot 后不會保留為什么寄存器地址后面的十六進制值的 bit [24]寫 1?因為該寄存器的 16bit 至31 bit 是寫有效位,默認(rèn)為 0,即不可寫。因為要往[8] bit 寫 1,所以[8] bit ?對應(yīng)的
- 寫有效位 16 bit 也要對應(yīng)置 1 才可寫入,這個是根據(jù)寄存器描述而定的。
修改過后,再查看,可以發(fā)現(xiàn)對應(yīng)的位的值變?yōu)榱?:
三、 通過函數(shù)gpiod_direction_output()
思 路 :驅(qū) 動 里 如 果 想 要 去 操 作 GPIO , 肯 定 會 調(diào) 用 到 gpio_direction_output 、gpio_direction_input、 gpiod_direction_output、 gpiod_direction_input 這幾個接口, 他們的定義位置是:
?vim?kernel/drivers/gpio/gpiolib.c
在這些接口里添加判斷對應(yīng)查詢的 IO 口條件,通過以下函數(shù)就可以打印出哪些模塊復(fù)用了該引腳:
dump_stack();?
注意:在 linux3.10 內(nèi)核的 sdk 中用的是 gpio_direction_output 和gpio_direction_input 接 口 ,
在 linux4.4 內(nèi) 核 中 則 是 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引腳驅(qū)動編寫》
-
- 最前面和 32 相乘的數(shù)字因為是 gpio4,所以是 4
32。如果是 gpio3,那就是 3
- 32;括號里面的 A、 B、 C、 D 分別代表數(shù)值 0、 1、 2、 3,在計算時候分別對應(yīng)去減即可,這里因為是 B3,所以用 B-A,如果是 C3,就是 C-A;最后的+3 是因為是 ? B3,如果是 GPIO4B2,那么最后就+2。
注:在 linux4.4 內(nèi)核, io 引腳的值有些變化,也就是按照上文算法計算的結(jié)
果要+1000,所以 GPIO4B3 如果是 linux4.4 內(nèi)核里要填 1139。
int?gpiod_direction_output(struct?gpio_desc?*desc,?int?value)
{
?if?(!desc?||?!desc->chip)?{
??pr_warn("%s:?invalid?GPIOn",?__func__);
??return?-EINVAL;
?}
+??if?(?desc_to_gpio(desc)?==?1139)
+??{
+???printk("dump_stack_startn");
+???dump_stack();
+???printk("dump_stack_endn");
+??}
?if?(test_bit(FLAG_ACTIVE_LOW,?&desc->flags))
??value?=?!value;
?return?_gpiod_direction_output_raw(desc,?value);
}
添加后編譯燒錄,只要對應(yīng)判斷的引腳有被調(diào)用,啟動 log 中就會打印出堆棧,可以根據(jù)找出結(jié)果查看,找到驅(qū)動調(diào)用函數(shù)。
四、通過函數(shù)rockchip_set_mux()
配置IOMUX會調(diào)用該接口,
仍然以引腳gpio4 b3為例。
[drivers/pinctrl/pinctrl-rockchip.c]
??@@?-1224,6?+1224,17?@@?static?int?rockchip_set_mux(struct?rockchip_pin_bank?*bank,?int?pin,?int?mux)
???dev_dbg(info->dev,?"setting?mux?of?GPIO%d-%d?to?%dn",
?????????????bank->bank_num,?pin,?mux);
+???????if((bank->bank_num?==?4)&&(pin?==?11)){
+???????????????printk("6902?setting?mux?of?GPIO%d-%d?to?%dn",
+???????????????????????????????bank->bank_num,?pin,?mux);
+???????????????dump_stack();
+???????}
????if?(bank->iomux[iomux_num].type?&?IOMUX_SOURCE_PMU)
????????????regmap?=?info->regmap_pmu;
- bank_num 表示gpio4這里“ pin ==”后面跟的值計算方式為:將 A0 至 D7 32 個引腳順序?qū)?yīng)數(shù)值 0 至 31,b3為11。
五、如何去掉設(shè)備樹中的復(fù)用引腳信息?
剛才分析,發(fā)現(xiàn)GPIO2 A2被SDMMC0占用,那么如何來解決這個沖突呢?
只要從設(shè)備樹下手即可。
瑞芯微平臺的設(shè)備樹,根據(jù)平臺區(qū)分,往往前綴是:
rk?+?平臺??+?板子型號?+?ddr型號?+?版本
比如rk3568系列設(shè)計的設(shè)備樹文件如下:
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版本,所以去掉其他的文件,
我們只需要關(guān)注以下文件即可。
arch/arm64/boot/dts/rockchip/rk3568-android.dtsi
與安卓相關(guān)的信息
arch/arm64/boot/dts/rockchip/rk3568.dtsi?
描述cpu、memory、timer、clk、sata、usb?host、gic、視頻控制器、sram、cru、i2c控制器、uart、pwm、pmu等各種Soc內(nèi)部硬件信息
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底板相關(guān)的外設(shè)硬件信息
arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi
pinctl相關(guān)硬件信息
還有1個描述pinctl引腳驅(qū)動能力的文件:
rockchip-pinconf.dtsi
使用grep命令來查詢:
grep?sdmmc0?arch/arm64/boot/dts/rockchip/rk3568*?-nr
好在信息不多,逐個查看,下面這個文件,我們找到了復(fù)用的地方。
rk3568-pinctrl.dtsi
該引腳被定義為sdmmc0_clk,作為時鐘被使用了。
?sdmmc0?{
??………………
??/omit-if-no-ref/
??sdmmc0_clk:?sdmmc0-clk?{
???rockchip,pins?=
????/*?sdmmc0_clk?*/
????<2?RK_PA2?1?&pcfg_pull_up_drv_level_2>;
??};
修改的方法有很多種:
投機取巧法
將sdmmc0_clk改成其他沒有用的gpio
簡單粗暴法
如果確定沒有使用sdmmc0,可以將所有sdmmc0地方全部注釋掉
設(shè)備樹支持下面這種方法:
#if?0
#endif
- 硬件飛線法
找硬件工程師飛線,改用其他的GPIO
-
- 最優(yōu)法
- 如果sdmmc0也用到了,
那就只能修改沖突的GPIO,
但是這種情況,往往會牽一發(fā)而動全身,
要改好幾處,那就需要各位老鐵細心慢慢修改了。
好了,本文到底結(jié)束。
一口君目標(biāo)是寫100篇瑞芯微平臺的文章,
有喜歡瑞芯微的老鐵,
歡迎大家關(guān)注學(xué)習(xí)。