IO复用排查以及IO工具的使用

一、 如何通过 IO 读寄存器确认引脚复用等问题
作者:lml
1、kernel 阶段使用 io 命令确认寄存器方法:
许多客户经常会碰到一些 IO 引脚虽然已经在 dts 中配置了对应的 iomux,但却发现
实际示波器测量出来信号不对。一般这种情况出现,可首先排查 IO 引脚是否被复用。
在 RK 的 Android 平台,默认有包含 io 工具(源码位置:external\io),linux 系统平台
默认没有此源码,如果想要使用,可以将 Android 平台此源码打包过去编译即可。
IO 工具使用方法:
Io 所包含的命令参数详解可以在串口或者 adb 输入:io? 回车后便可回显罗列出。
要查询 io 寄存器首先要有主控芯片详细规格书,可向我司 FAE 索取。
拿到规格书后,如何确认一个 IO 引脚是否被复用呢? 以下用 RK3399 平台来举例:
例如 想查询的是 GPIO4_B0 引脚,在原理图上是:
接下来在 datasheet 上搜索 gpio4b0(不带下杠,连着输入名称),可以找到寄存器:
在这里插入图片描述
此时顺着往上翻滚可知道该寄存器的基地址名称是“GRF”
在这里插入图片描述
下一步就是在 datasheet 中搜索 address mapping,并在其中找到 GRF 的基地址:
在这里插入图片描述
明确地址后,在串口或者 adb 输入:
io -4 -r 0xff77eo24
后回车可得到输出结果,以下举个例子:
rk3399_mid:/ # io -4 -r 0xff77e024
ff77e024: 00000555
那么输出结果就是 0x555,怎么看呢? 因为 0x555 是十六进制,我们把它转化为二进
制就是:0000 0000 0000 0000 0000 0101 0101 0101 ,这里写出完整的转化后的 32bit
方便大家观看,转换后的结果从右至左为低位到高位,即最右边的 bit 为第 0 bit,最左
边的 bit 为第 31bit。那么对应到之前查询到的寄存器 :GRF_GPIO4B_IOMUX 便可以一
一对应来查看结果了。
在 GRF_GPIO4B_IOMUX 寄存器中,gpio4b3 的功能寄存器是第 7 bit 和第 6 bit[7:6] :
在这里插入图片描述
而查看上面串口输出结果转换二进制后结果来看,[7:6] bit 就是 01,也就是对应该寄存
器的
01:sdmmc_data3 这个功能。由此,我们知道了 gpio4b3 这个 IO 引脚当前的复用情况,
如果这不是自己设置的状态,则去代码中查找哪里被复用即可。
那么,如何查找代码中的复用?
打个比方,比如上面的 gpio4b3 这个引脚,已经在 dts 中设置了对应的 iomux,作为一
个 gpio 去拉高拉低,但上面查询结果是 sdmmc,那么这时候我们首先要去 dts 中查找
sdmmc 节点先确认节点里是否有 pinctrl 里填错了 gpio,导致 mux 错了。绝大部分情况
的 IO 复用问题都可以在 dts 中找到问题点。
还有一种常见问题就是,一个 IO 引脚,客户想作为 GPIO 使用,按照上面方法确认了也
确实是 GPIO 的功能没错,但是示波器测量波形感觉没有按照自己驱动代码来拉高拉低,
有些“不受控”。这时候很可能就是有其它驱动也在操作这个 GPIO,导致信号紊乱。应
该怎么查:
很多客户喜欢去 gpio 节点下面查询,但是很多时候节点下面并不能显示具体是被什么
驱动调用。这里介绍一个个人常用的方法:
思 路 : 驱 动 里 如 果 想 要 去 操 作 GPIO , 肯 定 会 调 用 到 gpio_direction_output 、
gpio_direction_input、
gpiod_direction_output、gpiod_direction_input 这几个接口,他们的定义位置是:
vim kernel/drivers/gpio/gpiolib.c
如果我们能够在这些接口里添加判断对应查询的 IO 口条件,如果满足就打印出
dump_stack(); ,那么该 IO 口的调用就一目了然了。
这里需要说明一下的是,在 linux3.10 内核的 sdk 中用的是 gpio_direction_output 和
gpio_direction_input 接 口 , 而 在 linux4.4 内核中则是 gpiod_direction_output 和
gpiod_direction_input,功能不同,详情可查看源码了解。
下面罗列出 linux3.10 版本和 linux4.4 版本添加 dump_stack 条件的方法供参考使用:
这里我们还是使用 gpio4b3 来举例。首先,需要计算出代表 gpio4b3 的值,算法如下:
Gpio4_B3 = 4 32 + (B-A) * 8 + 3 = 3 32 + 1 * 8 + 3 = 139
(注:最前面和 32 相乘的数字因为是 gpio4,所以是 4
32,如果是 gpio3,那就是 3
32;;
括号里面的 A B C D 分别代表数值 0 1 2 3,在计算时候分别对应去减即可;最后的
+3 是因为是 GPIO4B3,如果是 GPIO4B2,那么最后就+2。)

Linux3.10
int gpio_direction_output(unsigned gpio, int value)
{
+ if (gpio == 139)
+ {
+ printk("dump_stack_start\n");
+ dump_stack();
+ printk("dump_stack_end\n");
+ }
 return gpiod_direction_output(gpio_to_desc(gpio), value);
}
Linux4.4
注:在 linux4.4 内核,io 引脚的值有些变化,也就是按照上文算法计算的结果要+1000,
所以 GPIO4B3 如果是 linux4.4 内核里要填 1139int gpiod_direction_output(struct gpio_desc *desc, int value)
{
 if (!desc || !desc->chip) {
 pr_warn("%s: invalid GPIO\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);
}

2、Uboot 阶段寄存器状态确认方法:
如果是想要确认 uboot 阶段寄存器状态,可以使用 IO 命令或者 md 命令都可以,一般
SDK 里都有带,不过默认没打开。 1、 如果是想像 kernel 一样使用 IO 命令,只需打开以下宏即可:

如果是想像 kernel 一样使用 IO 命令,只需打开以下宏即可:
diff --git a/include/configs/rk_default_config.h 
b/include/configs/rk_default_config.h
index 9852917d4f..305de26347 100755
--- a/include/configs/rk_default_config.h
+++ b/include/configs/rk_default_config.h
@@ -304,7 +304,7 @@
/* rk io command tool */
-#undef CONFIG_RK_IO_TOOL
+#define CONFIG_RK_IO_TOOL
/* rockusb */
2、 如果是想使用 MD 命令来查看寄存器,可以添加以下宏:
diff --git a/include/configs/rk33plat.h b/include/configs/rk33plat.h
index 0906c0c091..3f94e9eabe 100755
--- a/include/configs/rk33plat.h
+++ b/include/configs/rk33plat.h
@@ -140,6 +140,7 @@
 #define CONFIG_RK_GPIO_EXT_FUNC
 #define CONFIG_CHARGE_LED
 #define CONFIG_POWER_FUSB302
+ #define CONFIG_CMD_MEMORY
#endif
#if defined(CONFIG_RKCHIP_RK322XH)

然后make distclean重新选择配置编译即可,此时发现u-boot/common/cmd_mem.c
被编译。
IO 命令的话使用方法和上面介绍的 kernel 部分一样,这里不再做重复。下面就来介绍
md 命令使用方法:
在打开 MD 的宏后,在 uboot 代码:
diff --git a/common/autoboot.c b/common/autoboot.c
index c27cc2c751…fe28adb2fd 100644
— a/common/autoboot.c
+++ b/common/autoboot.c
@@ -146,6 +146,7 @@ static int abortboot_normal(int bootdelay)
{
int abort = 0;
unsigned long ts;

  • bootdelay = 3; //此处添加时间可自由定义,单位:秒
    #ifdef CONFIG_MENUPROMPT
    printf(CONFIG_MENUPROMPT);
    这样修改后编译烧录 uboot.img 在机器起来至打印 uboot 的:
    Hit any key to stop autoboot:
    这句会停下来倒数,倒数时间就是上面”bootdelay = 3;”设置的时间,设置多少就是几秒。
    只要在倒数时间内敲 PC 键盘任意按键便可让机器停在 uboot 阶段,这时候可使用 md
    命令:
    md 0x???(这里后面的问号即是寄存器地址,想查询什么寄存器就填对应地址)
    来读取想要查看的寄存器值。
    二、如何通过 IO 写寄存器
    如果客户想要通过 IO 命令临时写一个寄存器做实验,可以通过 io –w 去写。
    打个比方,已经通过命令: io -4 -r 0xff77e024 读出了寄存器的值,那么此时想对
    0xff77e024 这个寄存器的第 0 个 bit 写入 1,那么可以如下操作:
    Io -4 –w 0xff77e024 0x00010001
    注:为什么寄存器地址后面的十六进制值的第 16 bit 要写 1?因为该寄存器的 16bit 至
    31 bit 是写有效位,默认为 0,即不可写。因为要往第 0 bit 写 1,所以 0 bit 对应的
    写有效位 16 bit 也要对应置 1 才可写入,这个是根据寄存器描述而定的:
    在这里插入图片描述

注意:
一般系统不带IO 命令需要补丁

diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c
index 0b94b67..e363df4 100644
--- a/arch/arm/mm/mmap.c
+++ b/arch/arm/mm/mmap.c
@@ -228,7 +228,7 @@ int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
 	return (pfn + (size >> PAGE_SHIFT)) <= (1 + (PHYS_MASK >> PAGE_SHIFT));
 }
 
-#ifdef CONFIG_STRICT_DEVMEM
+#if 0
 
 #include <linux/ioport.h>
 
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
index 157f2ca..167cb65 100644
--- a/arch/arm64/mm/mmap.c
+++ b/arch/arm64/mm/mmap.c
@@ -135,7 +135,7 @@ int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
 	return !(((pfn << PAGE_SHIFT) + size) & ~PHYS_MASK);
 }
 
-#ifdef CONFIG_STRICT_DEVMEM
+#if 0
 
 #include <linux/ioport.h>
 
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 84a012e..6435534 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -573,7 +573,7 @@ static int __init add_system_ram_resources(void)
 }
 subsys_initcall(add_system_ram_resources);
 
-#ifdef CONFIG_STRICT_DEVMEM
+#if 0
 /*
  * devmem_is_allowed(): check to see if /dev/mem access to a certain address
  * is valid. The argument is a physical page number.
diff --git a/arch/unicore32/include/asm/io.h b/arch/unicore32/include/asm/io.h
index cb1d8fd..d1b25ae 100644
--- a/arch/unicore32/include/asm/io.h
+++ b/arch/unicore32/include/asm/io.h
@@ -48,7 +48,7 @@ extern void __uc32_iounmap(volatile void __iomem *addr);
 #define PIO_MASK		(unsigned int)(IO_SPACE_LIMIT)
 #define PIO_RESERVED		(PIO_OFFSET + PIO_MASK + 1)
 
-#ifdef CONFIG_STRICT_DEVMEM
+#if 0
 
 #include <linux/ioport.h>
 #include <linux/mm.h>
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index a3c9ea2..1befa1f 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -794,7 +794,7 @@ pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
 	return vma_prot;
 }
 
-#ifdef CONFIG_STRICT_DEVMEM
+#if 0
 /* This check is done in drivers/char/mem.c in case of STRICT_DEVMEM */
 static inline int range_is_allowed(unsigned long pfn, unsigned long size)
 {
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 54b8649..2d9ac3c 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -60,7 +60,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
 }
 #endif
 
-#ifdef CONFIG_STRICT_DEVMEM
+#if 0
 static inline int page_is_allowed(unsigned long pfn)
 {
 	return devmem_is_allowed(pfn);
@@ -881,7 +881,7 @@ static const struct memdev {
 	const struct file_operations *fops;
 	fmode_t fmode;
 } devlist[] = {
-#ifdef CONFIG_DEVMEM
+#if 1
 	 [1] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
 #endif
 #ifdef CONFIG_DEVKMEM
diff --git a/kernel/resource.c b/kernel/resource.c
index 8205d8e..c317417 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1525,7 +1525,7 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size)
 	return err;
 }
 
-#ifdef CONFIG_STRICT_DEVMEM
+#if 0
 static int strict_iomem_checks = 1;
 #else
 static int strict_iomem_checks;

arch下只要打对应的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值