• Horatiu Vultur's avatar
    pinctrl: ocelot: Fix interrupt controller · c297561b
    Horatiu Vultur authored
    When an external device generated a level based interrupt then the
    interrupt controller could miss the interrupt. The reason is that the
    interrupt controller can detect only link changes.
    
    In the following example, if there is a PHY that generates an interrupt
    then the following would happen. The GPIO detected that the interrupt
    line changed, and then the 'ocelot_irq_handler' was called. Here it
    detects which GPIO line saw the change and for that will call the
    following:
    1. irq_mask
    2. phy interrupt routine
    3. irq_eoi
    4. irq_unmask
    
    And this works fine for simple cases, but if the PHY generates many
    interrupts, for example when doing PTP timestamping, then the following
    could happen. Again the function 'ocelot_irq_handler' will be called
    and then from here the following could happen:
    1. irq_mask
    2. phy interrupt routine
    3. irq_eoi
    4. irq_unmask
    
    Right before step 3(irq_eoi), the PHY will generate another interrupt.
    Now the interrupt controller will acknowledge the change in the
    interrupt line. So we miss the interrupt.
    
    A solution will be to use 'handle_level_irq' instead of
    'handle_fasteoi_irq', because for this will change routine order of
    handling the interrupt.
    1. irq_mask
    2. irq_ack
    3. phy interrupt routine
    4. irq_unmask
    
    And now if the PHY will generate a new interrupt before irq_unmask, the
    interrupt controller will detect this because it already acknowledge the
    change in interrupt line at step 2(irq_ack).
    
    But this is not the full solution because there is another issue. In
    case there are 2 PHYs that share the interrupt line. For example phy1
    generates an interrupt, then the following can happen:
    1.irq_mask
    2.irq_ack
    3.phy0 interrupt routine
    4.phy1 interrupt routine
    5.irq_unmask
    
    In case phy0 will generate an interrupt while clearing the interrupt
    source in phy1, then the interrupt line will be kept down by phy0. So
    the interrupt controller will not see any changes in the interrupt line.
    The solution here is to update 'irq_unmask' such that it can detect if
    the interrupt line is still active or not. And if it is active then call
    again the procedure to clear the interrupts. But we don't want to do it
    every time, only if we know that the interrupt controller has not seen
    already that the interrupt line has changed.
    
    While at this, add support also for IRQ_TYPE_LEVEL_LOW.
    
    Fixes: be36abb7 ("pinctrl: ocelot: add support for interrupt controller")
    Signed-off-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
    Link: https://lore.kernel.org/r/20220909145942.844102-1-horatiu.vultur@microchip.comSigned-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
    c297561b
pinctrl-ocelot.c 57.9 KB