Search code examples
linuxioarmdriverisr

Could I/O memory access be used inside ISR under Linux (ARM)?


I'm creating driver for communication with FPGA under Linux. FPGA is connected via GPMC interface. When I tested read/write from driver context - everithing works perfectly. But the problem is that I need to read some address on interrupt. So I created interrupt handler, registred it and put iomemory reading in it (readw function). But when interrupt is fired - only zero's are readed. I tested every part of driver from the top to the bottom and it seems like the problem is in iomemory access inside ISR. When I replaced io access with constant value - it successfully passed to user-level application.

ARM version: armv7a (Cortex ARM-A8 (DM3730))

Compiler: CodeSourcery 2014.05

Here is some code from driver which represents performed actions:

// Request physical memory region for FPGA address IO
void* uni_PhysMem_request(const unsigned long addr, const unsigned long size) {
    // Handle to be returned
    void* handle = NULL;
    // Check if memory region successfully requested (mapped to module)
    if (!request_mem_region(addr, size, moduleName)) {
        printk(KERN_ERR "\t\t\t\t%s() failed to request_mem_region(0x%p, %lu)\n", __func__, (void*)addr, size);
    }
    // Remap physical memory
    if (!(handle = ioremap(addr, size))) {
        printk(KERN_ERR "\t\t\t\t%s() failed to ioremap(0x%p, %lu)\n", __func__, (void*)addr, size);
    }
    // Return virtual address;
    return handle;
}

// ...

// ISR
static irqreturn_t uni_IRQ_handler(int irq, void *dev_id) {
    size_t readed = 0;
    if (irq == irqNumber) {
        printk(KERN_DEBUG "\t\t\t\tIRQ handling...\n");
        printk(KERN_DEBUG "\t\t\t\tGPIO %d pin is %s\n", irqGPIOPin, ((gpio_get_value(irqGPIOPin) == 0) ? "LOW" : "HIGH"));
        // gUniAddr is a struct which holds GPMC remapped virtual address (from uni_PhysMem_request), offset and read size
        if ((readed = uni_ReadBuffer_IRQ(gUniAddr.gpmc.addr, gUniAddr.gpmc.offset, gUniAddr.size)) < 0) {
            printk(KERN_ERR "\t\t\t\tunable to read data\n");
        }
        else {
            printk(KERN_INFO "\t\t\t\tdata readed success (%zu bytes)\n", readed);
        }
    }
    return IRQ_HANDLED;
}

// ...

// Read buffer by IRQ
ssize_t uni_ReadBuffer_IRQ(void* physAddr, unsigned long physOffset, size_t buffSize) {
    size_t size = 0;
    size_t i;
    for (i = 0; i < buffSize; i += 2) {
        size += uni_RB_write(readw(physAddr + physOffset)); // Here readed value sent to ring buffer. When "readw" replaced with any constant - everything OK
    }
    return size;
}

Solution

  • Looks like the problem was in code optimizations. I changed uni_RB_write function to pass physical address and data size, also read now performed via ioread16_rep function. So now everything works just fine.