Re-enable lower prio interrupt during higher interrupt


We use a RZ/A2M, osless.

We have a higher prio (prio 5) interrupt, which may take some longer time in some cases.

Therefore we want to re-enable all interrupts ( this can be done by using __enable_irq() ), but also some interrupts with lower priority.

In many CPU's you can update the 'actual mask level' (which is also restored after the actual interrupt).
In that case, when you are in interrupt prio 5, you could update the masklevel to e.g. 7 (lower prio), so a new interrupt with prio 6 can interrupt the 'longer-lasting' interrupt with prio 5, but prio 8 cannot do it.

But in this ARM RZ/A2M we cannot find a solution to update this 'actual mask level'.

When we search on internet, the only way should be to end this interrupt already (by writing to INTC_GICC_EOIR_ADDR), and do a kind of 'simulate' a 'return from interrupt'.

But in that case, the interrupt with prio 8 is also enabled, thus you should have to do extra things like 'disable that interrupt for a while' or maybe by writing prio 7 to INTC.GICC_PMR.LONG temporarily.

The conclusion is thus: such operation looks not beeing so simple in the RZ/A2M.

Or does anyone have a brilliant idea ?

  • First of all RZ/A2M features an Arm GIC-400, that is an implementation of the Arm GIC version 2 architecture.
    So, this "problem" is applicable to all devices including a GIC-400, so many Arm based devices as you can deduce.

    Anyway I think the solution proposed is not that difficult: 

    - Configure the GICC_CTLR EOImode to 1, such that writing to GICC_EOIR just does the priority drop.
    You would also need to write to GICC_DIR to deactivate the interrupt.

    - Write to 7 into PMR to mask all interrupts with lower priority (>8).

    - Write the prio5 long lasting ID onto GICC_EOIR to only drop the priority.
    The interrupt would be deactivated only when the long lasting interrupt routine ends. 
    Also, when the long lasting interrupt routing ends, the PMR can be restored.

    Another common way to solve this problem, perhaps simpler, is to use SW interrupts. You can basically split the prio5 interrupt routine in two (or multiple) parts.
    When you are done with the most important / highest priority part, you just set a SW interrupt (with prio 7) and you return.
    The rest of the long interrupt routine will be served by the SW interrupt routine at prio 7, so that it can be interrupted by prio 6 but not by prio 8.

  • Although already some time ago, I still want to thank MicBis for the reaction.
    In the meanwhile, we tested following solution:

    We modified it in irqfiq_handler.asm, function irq_handler, between labels int_active and end_of_handler:

    Old behaviour:
    - Call INTC_Handler_Interrupt (C function)
    - Give End of Interrupt for this interrupt
    New behaviour:
    - Push priority mask register (PMR) on stack
    - Make PMR equal to Running priority register (PRR)
    - Give End of Interrupt for this interrupt.
        - From now, only higher priority interrupts are allowed again,
          but now with the ability to allow lower priorities by modifying the PMR register.
    - Call INTC_Handler_Interrupt (C function)
        - When modifying the PMR register to a just lower prioirty (higher value), recursion is possible.,
          and also lower prio interrupts can be allowed again.
        - And of course, call __enable_irq to allow any interrupt again.  
    - Pop PMR back from stack
        - From now, the PMR is always the same as when this interrupt was entered.

    We have tested this, and looks operating fine.