I am working on a simple module to read and write registers on the FPGA side. I am aware that this could be done with UIO and devmem but I would prefer to avoid using mmap on userland. Anyways I have created a kernel module that compiles fine and I have checked that the memory specified in the device tree is actually reserved and mapped in /proc/iomem and /proc/vmallocinfo. Inside my module, I access my IOCTL commands successfully but at the moment of performing writes or reads on the device through the virtual memory the values are not as expected.
Module IOCTL:
static long int device_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_param) {
int ret = SUCCESS;
struct comblock_local * lp = file->private_data;
struct user_params params;
if (_IOC_TYPE(cmd) != COMBLOCK_IOC_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) > COMBLOCK_IOC_MAXNR)
return -ENOTTY;
if(_IOC_DIR(cmd) & _IOC_READ)
ret = !access_ok((void __user*) ioctl_param, _IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
ret = !access_ok((void __user*) ioctl_param, _IOC_SIZE(cmd));
if(ret)
return -EFAULT;
if(down_interruptible(&lp->sem))
return -ERESTARTSYS;
printk(KERN_INFO "Comblock: Command %d", cmd & 0x7);
/* Switch according to the ioctl called */
switch (cmd) {
case COMBLOCK_IOC_REG_READ :
/* Read any register */
if(lp->reg_in_enable) {
ret = copy_from_user(¶ms, (struct user_params *) ioctl_param, sizeof(params));
if(ret)
goto err;
if (params.reg >= lp->reg_in_length) {
ret = 1;
goto err;
}
params.data[0] = ioread32(lp->axi_l_base_addr + params.reg * 4);
ret = copy_to_user((int __user *)ioctl_param, ¶ms, sizeof(params));
if(ret)
goto err;
}
else
goto err;
break;
case COMBLOCK_IOC_REG_WRITE :
if(lp->reg_out_enable) {
ret = copy_from_user(¶ms, (struct user_params *) ioctl_param, sizeof(params));
if(ret)
goto err;
if(params.reg >= lp->reg_out_length){
ret = 1;
goto err;
}
iowrite32(params.data[0],lp->axi_l_base_addr + params.reg * 4 + 16 * 4);
}
else
goto err;
break;
}
/* We're now ready for our next caller */
up(&lp->sem);
return ret;
err:
return ret;
}
cat /proc/iomem
80000000-8003ffff : comblock_dev
80040000-8004ffff : comblock_dev
cat /proc/vmallocinfo
0x00000000062b00d8-0x0000000094c99506 266240 device_probe+0x560/0x8cc [ComBlock_1] phys=0x0000000080000000 ioremap
0x000000006fe944d4-0x00000000a7e1f0db 69632 device_probe+0x5fc/0x8cc [ComBlock_1] phys=0x0000000080040000 ioremap
I am not sure if the problem has anything to do with how memory is allocated. Anyways here is how memory is requested and allocated in side the probe function :
if(lp->reg_in_enable || lp->reg_out_enable || lp->fifo_in_enable || lp->fifo_out_enable) {
axi_l = platform_get_resource(pdev, IORESOURCE_MEM, mem_index);
mem_index++;
if(!axi_l) {
ret= -ENOMEM;
goto out_free_local;
}else if(!request_mem_region(axi_l->start, axi_l->end - axi_l->start + 1, DEVICE_NAME)) {
dev_err(dev, "Couldn't lock memory region at %llu\n", axi_l->start);
ret = -EBUSY;
goto out_free_local;
}
lp->axi_l = axi_l;
lp->axi_l_base_addr = ioremap(axi_l->start, axi_l->end - axi_l->start + 1);
}
if(lp->ram_enable) {
axi_f = platform_get_resource(pdev, IORESOURCE_MEM, mem_index);
if(!axi_f) {
ret= -ENOMEM;
goto out_free_local;
}else if(!request_mem_region(axi_f->start, axi_f->end - axi_f->start + 1, DEVICE_NAME)) {
dev_err(dev, "Couldn't lock memory region at %llu\n", axi_f->start);
ret = -EBUSY;
goto out_free_local;
}
lp->axi_f = axi_f;
lp->axi_f_base_addr = ioremap(axi_f->start, axi_f->end - axi_f->start + 1);
}
dev_set_drvdata(dev, lp);
if(!lp->axi_l_base_addr && !lp->axi_f_base_addr) {
dev_err(dev, "Could not allocate iomem\n");
ret = -EIO;
goto out_free_mem_region;
}
Inside the FPGA the registers are in a loop so the output register 0 value gets copied to the input register 0. What I have noticed is that input reg 0 changes randomly. I printed the addresses that are supposedly being accessed when reading and writing on register 0:
In reg0: 0x00000000062B00D8
Out reg0: 0x00000000995d2734
Physically In reg0 and Out reg 0 are 16 addresses away so I naively used an offset of 16*4. I am using a 64-bit AXI between PS and AXI switch and after the switch, it goes to 32-bit. I appreciate any help with this. I have run out of ideas and even replicated the memory access done in the AXIGPIO Linux driver without luck.