MPSoC IOCTL physical memory access fails

Viewed 16

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(&params, (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, &params, sizeof(params));
            if(ret)
                goto err;
        }
        else
            goto err;
        break;
    case COMBLOCK_IOC_REG_WRITE :
        if(lp->reg_out_enable) {
            ret = copy_from_user(&params, (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.

0 Answers
Related