关于IMX6ULL 用户空间操作寄存器 死机的情况

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

关于IMX6ULL 用户空间操作寄存器 死机的情况

Jump to solution
5,847 Views
dizhuyuan
Contributor III

各位好!

   内核与交叉编译器 Linux version 4.1.15_2.1.0,gcc version 4.9.4 (Linaro GCC 4.9-2017.01)。

   在配置操作EIM总线和FPGA通信时,发现在用户空间使用/dev/mem访问总线地址会导致死机;

类似于:

# ./memtool -32 0x58000000 29
E
Reading 0x29 count starting at address 0x58000000

系统已经不响应了。

读GPIO寄存器

# ./memtool GPIO1.*
SOC: i.MX6ULL
GPIO1 Addr:0x209c000
vm_end = 0x76f0e000 vm_start = 0x76f0d000---------------
GPIO1.DR Addr:0x0209C000 Value:0x0008030E - The 32-bit GPIO_DR register stores data that is ready to be driven to the output lines.
GPIO1.DR.DR(0..31) :0x8030e
Data bits.

GPIO1.GDIR Addr:0x0209C004 Value:0x00000200 - GPIO_GDIR functions as direction control when the IOMUXC is in GPIO mode.
GPIO1.GDIR.GDIR(0..31) :0x200
GPIO direction bits.

GPIO1.PSR Addr:0x0209C008 Value:0x0008010E - GPIO_PSR is a read-only register.
GPIO1.PSR.PSR(0..31) :0x8010e
GPIO pad status bits (status bits).

GPIO1.ICR1 Addr:0x0209C00C Value:0x00000000 - GPIO_ICR1 contains 16 two-bit fields, where each field specifies the interrupt configuration for a different input signal.
GPIO1.ICR1.ICR0(0..1) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR1(2..3) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR2(4..5) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR3(6..7) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR4(8..9) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR5(10..11) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR6(12..13) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR7(14..15) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR8(16..17) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR9(18..19) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR10(20..21) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR11(22..23) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR12(24..25) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR13(26..27) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR14(28..29) :0x0
Interrupt configuration 1 fields.
GPIO1.ICR1.ICR15(30..31) :0x0
Interrupt configuration 1 fields.

GPIO1.ICR2 Addr:0x0209C010 Value:0x00000000 - GPIO_ICR2 contains 16 two-bit fields, where each field specifies the interrupt configuration for a different input signal.
GPIO1.ICR2.ICR16(0..1) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR17(2..3) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR18(4..5) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR19(6..7) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR20(8..9) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR21(10..11) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR22(12..13) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR23(14..15) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR24(16..17) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR25(18..19) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR26(20..21) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR27(22..23) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR28(24..25) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR29(26..27) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR30(28..29) :0x0
Interrupt configuration 2 fields.
GPIO1.ICR2.ICR31(30..31) :0x0
Interrupt configuration 2 fields.

GPIO1.IMR Addr:0x0209C014 Value:0x00000000 - GPIO_IMR contains masking bits for each interrupt line.
GPIO1.IMR.IMR(0..31) :0x0
Interrupt Mask bits.

GPIO1.ISR Addr:0x0209C018 Value:0xFFF7FEF1 - The GPIO_ISR functions as an interrupt status indicator.
GPIO1.ISR.ISR(0..31) :0xfff7fef1
Interrupt status bits - Bit n of this register is asserted (active high) when the active condition (as determined by the corresponding ICR bit) is detected on the GPIO input and is waiting for service.

GPIO1.EDGE_SEL Addr:0x0209C01C Value:0x00000000 - GPIO_EDGE_SEL may be used to override the ICR registers' configuration.
GPIO1.EDGE_SEL.GPIO_EDGE_SEL(0..31) :0x0
Edge select.

读EIM寄存器

# ./memtool EIM.*
SOC: i.MX6ULL
EIM Addr:0x21b8000

无响应了

使用memtool工具操作gpio寄存器确可以,读取EIM的寄存器确会出现系统无响应的情况,这个问题已经困扰我两天了,希望有遇到过的同仁可以帮忙解答下,谢谢。

Labels (1)
0 Kudos
1 Solution
5,464 Views
dizhuyuan
Contributor III

我找到是什么问题了, 是因为EIM的时钟没有使能导致的,但不清楚EIM时钟为什么没有使能,dts文件中也没有发现错误。通过查看 CCM_CCGR6 寄存器bit10-bit11 发现的。

验证方法:用户空间 通过devmem 0x020c4080 查看CCM_CCGR6 寄存器,devmem 0x020c4080 32 0x00cc3fc3 使能EIM时钟。

现在已经可以正常访问FPGA了。

@Wigros Sun  谢谢帮忙。

View solution in original post

0 Kudos
14 Replies
5,465 Views
dizhuyuan
Contributor III

我找到是什么问题了, 是因为EIM的时钟没有使能导致的,但不清楚EIM时钟为什么没有使能,dts文件中也没有发现错误。通过查看 CCM_CCGR6 寄存器bit10-bit11 发现的。

验证方法:用户空间 通过devmem 0x020c4080 查看CCM_CCGR6 寄存器,devmem 0x020c4080 32 0x00cc3fc3 使能EIM时钟。

现在已经可以正常访问FPGA了。

@Wigros Sun  谢谢帮忙。

0 Kudos
5,464 Views
weidong_sun
NXP TechSupport
NXP TechSupport

不用客气!

good job!

Weidong

0 Kudos
5,465 Views
weidong_sun
NXP TechSupport
NXP TechSupport

Hi,

FPGA接的是WEIM,你访问的地址有点问题。看imx6ull.dtsi中的定义:

            weim: weim@021b8000 {
                compatible = "fsl,imx6ul-weim", "fsl,imx6q-weim";
                reg = <0x021b8000 0x4000>;
                interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&clks IMX6UL_CLK_DUMMY>;
            };

Weidong

0 Kudos
5,465 Views
dizhuyuan
Contributor III

你说的时58000000地址吗?那个是我写错了的。我今天又做了测试,怀疑是虚拟地址与物理地址转换的问题。

在内核驱动中进行如下测试

void *virt = kmalloc(1024, GFP_KERNEL);

unsigned long phys = virt_to_phys(virt);

printk("virt = %p\n", virt);

printk("phys = %lx\n", phys);

virt = phys_to_virt(phys);

printk("phys = %lx\n", phys);

printk("virt = %p\n", virt);

输出:

virt = 8c4fe400
phys = 8c4fe400
phys = 8c4fe400
virt = 8c4fe400

地址没有变换?

找到转换的代码  arch/arm/include/asm/memory.h文件

并且跟踪到该宏    #ifdef CONFIG_NEED_MACH_MEMORY_H 

百度了下是新特性,还在了解这个新特性是怎么工作的。  

0 Kudos
5,465 Views
weidong_sun
NXP TechSupport
NXP TechSupport

我和同事讨论了一下,你试试这个地址行不行:5000_0000。

Weidong

0 Kudos
5,465 Views
dizhuyuan
Contributor III

试过了,还是一样的现象。

0 Kudos
5,465 Views
dizhuyuan
Contributor III

# ./memtool -16 0x50000000 8
E
Reading 0x8 count starting at address 0x50000000

/drivers/char/mem.c, 52 vm_end = 0x76f49000 vm_start = 0x76f48000---------------

系统不响应了。  刚刚笔误

0 Kudos
5,465 Views
weidong_sun
NXP TechSupport
NXP TechSupport

虚拟地址和物理地址转换有问题的可能性不大,否则你的GPIO寄存器也读不出来。

021b8000这个地址的连续16K,可以读写吗?

 weidong

0 Kudos
5,465 Views
dizhuyuan
Contributor III

我把驱动编译进内核可以正确的读取EIM寄存器和5000_0000. 暂时只能先这样调试了。

0 Kudos
5,465 Views
weidong_sun
NXP TechSupport
NXP TechSupport

OK,good job.

0 Kudos
5,465 Views
dizhuyuan
Contributor III

把ioreamp改为ioremap_nocache测试,结果还是一样 。。。。。。

0 Kudos
5,465 Views
dizhuyuan
Contributor III

上个测试我发现imx-weim.c 里面申请的虚拟地址和直接用ioremap 申请的地址偏移是不一样的。

随添加打印

void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
{
resource_size_t size;
const char *name;
void __iomem *dest_ptr;

BUG_ON(!dev);

if (!res || resource_type(res) != IORESOURCE_MEM) {
dev_err(dev, "invalid resource\n");
return IOMEM_ERR_PTR(-EINVAL);
}

size = resource_size(res);
name = res->name ?: dev_name(dev);

if (!devm_request_mem_region(dev, res->start, size, name)) {
dev_err(dev, "can't request region for resource %pR\n", res);
return IOMEM_ERR_PTR(-EBUSY);
}

if (res->flags & IORESOURCE_CACHEABLE)
{
printk(KERN_CRIT "devm_ioremap-----------------------------\r\n");
dest_ptr = devm_ioremap(dev, res->start, size);
}
else
{
printk(KERN_CRIT "devm_ioremap_nocache-----------------------------\r\n");
dest_ptr = devm_ioremap_nocache(dev, res->start, size);
}
if (!dest_ptr) {
dev_err(dev, "ioremap failed for resource %pR\n", res);
devm_release_mem_region(dev, res->start, size);
dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
}

return dest_ptr;
}

得出的结果是 imx-weim.c里面使用的是devm_ioremap_nocache 申请的虚拟内存。而我的测试代码使用的是ioremap。

devm_ioremap 使用的是ioremap

void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
resource_size_t size)
{
void __iomem **ptr, *addr;

ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;

addr = ioremap(offset, size);
if (addr) {
*ptr = addr;
devres_add(dev, ptr);
} else
devres_free(ptr);

return addr;
}

devm_ioremap_nocache 使用的是ioremap_nocache

void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
resource_size_t size)
{
void __iomem **ptr, *addr;

ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;

addr = ioremap_nocache(offset, size);
if (addr) {
*ptr = addr;
devres_add(dev, ptr);
} else
devres_free(ptr);

return addr;
}

0 Kudos
5,465 Views
dizhuyuan
Contributor III

在模块初始化的时候ioremap,在用户空间调用该模块读5000_0000系统也会无响应.... 

0 Kudos
5,465 Views
dizhuyuan
Contributor III

有个奇怪的现象,在imx-weim.c 中可以对EIM的地址进行读写,测试代码:

static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
const struct imx_weim_devtype *devtype)
{
u32 cs_idx, value[devtype->cs_regs_count];
int i, ret;

/* get the CS index from this child node's "reg" property. */
ret = of_property_read_u32(np, "reg", &cs_idx);
if (ret)
return ret;

if (cs_idx >= devtype->cs_count)
return -EINVAL;

ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
value, devtype->cs_regs_count);
if (ret)
return ret;

printk(KERN_CRIT "vitrual eim addr base = %p\r\n", base);
/* set the timing for WEIM */
for (i = 0; i < devtype->cs_regs_count; i++)
{
writel(value[i], base + cs_idx * devtype->cs_stride + i * 4);

printk(KERN_CRIT "data[%d] = %#x\r\n", i, readl(base + cs_idx * devtype->cs_stride + i * 4));
}
return 0;

输出:

vitrual eim addr base = 90920000
data[0] = 0x2a10001
data[1] = 0x0
data[2] = 0x10337422
data[3] = 0x0
data[4] = 0x106ba692
data[5] = 0x0
vitrual eim addr base = 90920000
data[0] = 0x2a10001
data[1] = 0x0
data[2] = 0x10337422
data[3] = 0x0
data[4] = 0x106ba692
data[5] = 0x0

自己通过ioremap的确不行,ioremap可以得到虚拟地址,但是在访问的时候系统就不响应了。

test_addr = ioremap(0x0209c000, 0x4000);
test_addr1 = ioremap(0x021b8000, 0x4000);
test_addr2 = ioremap(0x50000000, 0x4000);

if(IS_ERR(test_addr)) {
printk(KERN_CRIT "%s:%d ioremap failed\r\n", __func__, __LINE__);
} else {
printk(KERN_CRIT "%s:%d ioremap ok\r\n", __func__, __LINE__);
}

printk(KERN_CRIT "%s:%d gpio vitrual address: %p\r\n", __func__, __LINE__, test_addr);
printk(KERN_CRIT "%s:%d gpio virt_to_phys: %#x\r\n", __func__, __LINE__, virt_to_phys(test_addr));
printk(KERN_CRIT "%s:%d fpga vitrual address: %p\r\n", __func__, __LINE__, test_addr2);
printk(KERN_CRIT "%s:%d fpga virt_to_phys: %#x\r\n", __func__, __LINE__, virt_to_phys(test_addr2));
printk(KERN_CRIT "%s:%d eim vitrual address: %p\r\n", __func__, __LINE__, test_addr1);
printk(KERN_CRIT "%s:%d eim virt_to_phys: %#x\r\n", __func__, __LINE__, virt_to_phys(test_addr1));

printk(KERN_CRIT "%s:%d gpio read: 0x%04x\r\n", __func__, __LINE__, readl(test_addr + 0));
printk(KERN_CRIT "%s:%d fpga read: 0x%04x\r\n", __func__, __LINE__, readl(test_addr2 + 0));
printk(KERN_CRIT "%s:%d eim read: 0x%04x\r\n", __func__, __LINE__, readl(test_addr1 + 0 ));

输出:

eim2fpga_init:125
eim2fpga_init:134 ioremap ok
eim2fpga_init:137 gpio vitrual address: 90ac0000
eim2fpga_init:138 gpio virt_to_phys: 0x90ac0000
eim2fpga_init:139 fpga vitrual address: 90ad0000
eim2fpga_init:140 fpga virt_to_phys: 0x90ad0000
eim2fpga_init:141 eim vitrual address: 90ac8000
eim2fpga_init:142 eim virt_to_phys: 0x90ac8000
eim2fpga_init:144 gpio read: 0x8030e

0 Kudos