各位好!
内核与交叉编译器 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的寄存器确会出现系统无响应的情况,这个问题已经困扰我两天了,希望有遇到过的同仁可以帮忙解答下,谢谢。
已解决! 转到解答。
我找到是什么问题了, 是因为EIM的时钟没有使能导致的,但不清楚EIM时钟为什么没有使能,dts文件中也没有发现错误。通过查看 CCM_CCGR6 寄存器bit10-bit11 发现的。
验证方法:用户空间 通过devmem 0x020c4080 查看CCM_CCGR6 寄存器,devmem 0x020c4080 32 0x00cc3fc3 使能EIM时钟。
现在已经可以正常访问FPGA了。
@Wigros Sun 谢谢帮忙。
我找到是什么问题了, 是因为EIM的时钟没有使能导致的,但不清楚EIM时钟为什么没有使能,dts文件中也没有发现错误。通过查看 CCM_CCGR6 寄存器bit10-bit11 发现的。
验证方法:用户空间 通过devmem 0x020c4080 查看CCM_CCGR6 寄存器,devmem 0x020c4080 32 0x00cc3fc3 使能EIM时钟。
现在已经可以正常访问FPGA了。
@Wigros Sun 谢谢帮忙。
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
你说的时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
百度了下是新特性,还在了解这个新特性是怎么工作的。
上个测试我发现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;
}
有个奇怪的现象,在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