Using interrupts in a linux device driver

Hello!
I am developing a GPT linux device driver for a specific project we have. I have a working driver that can configure, start and stop the timer, and various other things. The next thing I need is to trigger an interrupt on an input capture event. The input capture works and I have enabled the IC1IE flag in the GPT.

The linux-device-driver registers the irq routine using the following snippet:

uint8_t minor_num = 1;
uint8_t* minor_num_p = &minor_num;
if (request_irq(IRQ_NUMBER, irq_handler, IRQF_SHARED, "gpt_device", (void *)(minor_num_p))) {
        printk(KERN_ALERT "FAILED TO SETUP IRQ ");
        goto fail;
}

The irq_handler function is a simple printk statement for now.

In /proc/interrupts the handler seems to have been registered as there is a new entry:

 IRQ_NUMBER:          0          0          0          0          0          0     GICv3 442 Level     gpt_device

The device-tree entry for the gpt has an interrupts parameters:

    lsio_gpt1: gpt@5d150000 {
        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;    
       ....
    };

I believe this one should be 18 since the apalis iMX8 datasheet says that the ALT3 function is _IO18.

I am very unsure of the IRQ_NUMBER for the linux-device-driver (a bit for the device-tree as well). According to the i.MX8QM reference manual, GPT1_INT is 113, but I have not gotten the interrupt to work yet. Is there another value I should use? Or are there any other settings that I need to set in hardware? I tried to look into the GIC and IRQ_STEER as those where the ones that seemed to control the interrupts, but they seem to be more for the m4 cores. I am used to having to modify an NVIC when working on interrupts, but have not found anything like that for the iMX8QM

Hardware details:
Apalis IMX8QM using the ixoraV1.2 board

Best regards,

Hi @aleksw

I guess you want to get the interrupt for GPT1 if I understand you correctly not for a GPIO, right?

According to the reference manual that would be GIC_SPI 113 as you write. However, you have to subtract 32 to map with the NXP Linux numbering. So it would be:

    lsio_gpt1: gpt@5d150000 {
        interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;    
       ....
    };

Regards,
Stefan

Thanks for your response @stefan_e.tx!

Alrighty, I didnt know about the subtraction of 32. Then the values of the device-tree actually makes a lot more sense.
But what number should I use for ny linux driver then? I tried to use 81 as the IRQ_NUMBER in the request_irq(), but then I get this error:

[    5.484001] genirq: Flags mismatch irq 81. 00000084 (gpt_device) vs. 00000004  (5b040000.ethernet)

Hi @aleksw

You shouldn’t just put a number in there. The device has several interrupt controllers that’s why you have to use the device tree to get to the right number. Use e.g. platform_get_irq_byname to get the correct interrupt:

I would recommend you to get familiar with device trees, else it will be hard to succeed with the driver. I can recommend you Bootlin as a training partner for these topics:

Regards,
Stefan

Hello @aleksw,

I hope you are doing well. May I know if you were able to make any progress on this topic?

So @stefan_e.tx was onto something with the suggestion of using the function platform_get_irq_by_name, however, since I am developing a normal linux device driver and not a platform driver, Im not sure that I could use this method. There is a different set of functions that can be used to get information from the device-tree and map that into linux values. So I got the interrupt to work in the using the following:

struct device_node* device_tree_node = NULL;
device_tree_node = of_find_node_by_name( NULL, "gpt1" );
if ( device_tree_node == NULL ) {
  printk( KERN_ALERT "Device tree node: %s is not defined in DTS\n", "gpt1" );
  return -1;
}
int irq_number = irq_of_parse_and_map( device_tree_node, 0 );
if ( request_irq(irq_number, irq_handler, IRQF_SHARED, "interrupt_routine_name",
(void*)( &interrupt_data ) ) ) {
  printk( KERN_ALERT "FAILED TO SETUP IRQ %i", irq_number );
  return -1;
}

Where gpt1 is the node name infront of the physical memory specialization(I was not able to use any other names or labels to acquire this information)

lsio_gpt1: gpt1@5d150000 {
    interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
};

And the irq_handler has a signature of:

static irqreturn_t irq_handler( int irq, void* dev_id ) {}

In the end, using interrupts for measuring the frequency of an external signal was not a reliable method when the frequency got too high. So i ended up using the external signal as the an external clock for the signal and then counting the number of ticks that happens in a given amount of time, which works surprisingly well :raised_hands: