Proper GPIO interrupt handling in iMX6

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Proper GPIO interrupt handling in iMX6

8,386 Views
nathanpalmer
Contributor IV

I am using the iMX6 BSP and have a question about interrupt handling.

As I understand the flow in the BSP, the following happens on GPIO interrupt (assuming a handler is installed with register_interrupt_routine(IRQ, ISR):

1. ARM Core takes IRQ Exception and globally masks the IRQ bit in the CPSR (Disables IRQ interrupts)

2. The Freescale IRQ_HDLR() is called because it is in the ARM vector for IRQ exception

3. The GIC is checked for the highest priority interrupt pending and the GIC is ACKed

4. That interrupt is called by looking up the address (in this case, handle_GPIO())

5. The GIC is signafied that the interrupt is handled

6. ARM core is restored to resume "mainline" execution

My question is: What should happen in handle_GPIO() to ensure safe interrupt handling? Specifically, when should I ACK the status in the GPIO_ISR register to make sure interrupts are not missed, and should I mask the GPIO_IMR register while handling the interrupt?

Currently i am doing this:

  uint32_t mask = HW_GPIO_IMR_RD(port);

  HW_GPIO_IMR_WR(port, mask & ~(1UL << pin)); // Mask the interrupt

  uint32_t status = HW_GPIO_ISR_RD(port) ; // Read the status

  HW_GPIO_ISR_WR(port, (1UL << pin)); // ACK the status

  if (status & mask & (1UL << pin) ) {

       // Call the ISR function that is assigned to this pin

       gpio_irqs_in_use[i].isr_func();

  }

  HW_GPIO_IMR_WR(port, mask); // Un-mask the interrupt

But I don't know if it is correct.  Also, I would like to be able to mask the interrupt FROM the isr_func() which is impossible with this approach.

Labels (2)
4 Replies

2,899 Views
igorpadykov
NXP Employee
NXP Employee

Hi Nathan

I would suggest to look at attached Linux Manual (L3.0.35_4.1.0)

sect.2.2.4 Interrupt Source Code Structure and files where implemented

interrupt processing, for example struct irq_chip gpio_irq_chip, gpio.c

Best regards

igor

-----------------------------------------------------------------------------------------------------------------------

Note: If this post answers your question, please click the Correct Answer button. Thank you!

-----------------------------------------------------------------------------------------------------------------------

0 Kudos

2,899 Views
nathanpalmer
Contributor IV

What is the purpose of masking the interrupt (IMR register) while calling the ISR (call to generic_handle_irq(...)?  This appears to be what the Linux code you attached is doing, but it is a little unclear.

I am not masking the IMR while handling the ISR and I would like to make sure that is OK for this chip.

0 Kudos

2,899 Views
andreatessadri
Contributor I

Hi Nathan,

  I have exactly the same problem like yours.

I found the solution changing the default IRQ flow handler for the GPIO-MXC IRQ controller from "level flow" to "egde flow".

In the file gpio-mxc.c (at <kernel-build-dir>/drivers/gpio) there is the function responsible for the gpio interrupt controller initialization:

static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)

{

   struct irq_chip_generic *gc;

   struct irq_chip_type *ct;

   gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base,

                port->base, handle_level_irq);

   ...

change this initialization with the following one:

   gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base,

                port->base, handle_edge_irq);

In this way everytime an edge interrupt is detected it will be properly handled by the function handle_edge_irq (file: <kernel-build-dir>/kernel/irq/chip.c).

https://www.kernel.org/doc/htmldocs/genericirq/Highlevel_IRQ_flow_handlers.html

The interrupt is kept enabled and is masked in the flow handler when an interrupt event happens. This prevents losing edge interrupts on hardware which does not store an edge interrupt event while the interrupt is disabled at the hardware level. When an interrupt arrives while the IRQ_DISABLED flag is set, then the interrupt is masked at the hardware level and the IRQ_PENDING bit is set. When the interrupt is re-enabled by enable_irq() the pending bit is checked and if it is set, the interrupt is resent either via hardware or by a software resend mechanism. (It's necessary to enable CONFIG_HARDIRQS_SW_RESEND when you want to use the delayed interrupt disable feature and your hardware is not capable of retriggering.

0 Kudos

2,899 Views
nathanpalmer
Contributor IV

Thanks, that is actually the code I looked at to get my implementation (from the current Kernel git repo).  However, I was unclear about the chained_irq_enter(chip, desc); and chained_irq_exit(chip, desc) functions.  They appear to mask the specific pin interrupt (GPIO_IMR) while handling it and then restore IMR when done.  I was wondering if that is necessary, since interrupts are globally masked while in IRQ exception mode (correct?).

The reason I ask is that I need to be able to mask a GPIO interrupt from a GPIO ISR.  For example, wait for a rising edge and then when it happens stop looking for more edges.  I am tempted to not mask/restore the IMR while handling the ISR, that way I can mask the IMR in the ISR. I just want to make sure I am following best practices to prevent ISR problems. 

0 Kudos