Граф коммитов

3 Коммитов

Автор SHA1 Сообщение Дата
Lars-Peter Clausen 6dd8595083 gpio: zynq: Fix IRQ handlers
The Zynq GPIO interrupt handling code as two main issues:
1) It does not support IRQF_ONESHOT interrupt since it uses handle_simple_irq()
for the interrupt handler. handle_simple_irq() does not do masking and unmasking
of the IRQ that is required for this chip to be able to support IRQF_ONESHOT
IRQs, causing the CPU to lock up in a interrupt storm if such a interrupt is
requested.
2) Interrupts are acked after the primary interrupt handlers for all asserted
interrupts in a bank have been called. For edge triggered interrupt this is to
late and may cause a interrupt to be missed. For level triggered oneshot
interrupts this is to early and causes the interrupt handler to run twice per
interrupt.

This patch addresses the issue by updating the driver to use the correct IRQ
chip handler functions that are appropriate for this kind of IRQ controller.

The following diagram gives an overview of how the interrupt detection circuit
works, it is not necessarily a accurate depiction of the real hardware though.

     INT_POL/INT_ON_ANY
           |
           | +---+                       INT_STATUS
           `-|   |                            |
             | E |-.                          |
         ,---|   |  \     |\          +----+  |  +---+
         |   +---+   `----| | ,-------|S   | ,*--|   |
GPIO_IN -*                | |-        |   Q|-    | & |-- IRQ_OUT
         |   +---+  ,-----| |       ,-|R   |   ,o|   |
         `---|   | /      |/       |  +----+  |  +---+
             | = |-        |       |          |
           ,-|   |    INT_TYPE    ACK     INT_MASK
           | +---+
           |
        INT_POL

GPIO_IN is the raw signal level connected to the hardware pin. This signal is
routed to a edge detector and to a level detector. The edge detector can be
configured to either detect a rising or falling edge or both edges. The level
detector can detect either a level high or level low event. Depending on the
setting of the INT_TYPE register either the edge or level event will be
propagated to the INT_STATUS register. As long as a interrupt condition is
detected the INT_STATUS register will be set to 1. It can be cleared to 0 if
(and only if) the interrupt condition is no longer detected and software
acknowledges the interrupt by writing a 1 to the address of the INT_STATUS
register. There is also the INT_MASK register which can be used to disable the
propagation of the INT_STATUS signal to the upstream IRQ controller. What is
important to note is that the interrupt detection logic itself can not be
disabled, only the propagation of the INT_STATUS register can be delayed. This
means that for level type interrupts the interrupt must only be acknowledged
after the interrupt source has been cleared otherwise it will stay asserted and
the interrupt handler will be run a second time. For IRQF_ONESHOT interrupts
this means that the IRQ must only be acknowledged after the threaded interrupt
has finished running. If a second interrupt comes in between handling the first
interrupt and acknowledging it the external interrupt will be asserted, which
means trying to acknowledge the first interrupt will not clear the INT_STATUS
register and the interrupt handler will be run a second time when the IRQ is
unmasked, so no interrupts will be lost. The handle_fasteoi_irq() handler in
combination with the IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED flags will
have the desired behavior. For edge triggered interrupts a slightly different
strategy is necessary. For edge triggered interrupts the interrupt condition is
only true when the edge itself is detected, this means this is the only time the
INT_STATUS register is set, acknowledging the interrupt any time after that will
clear the INT_STATUS register until the next interrupt happens. This means in
order to not loose any interrupts the interrupt needs to be acknowledged before
running the interrupt handler. If a second interrupt occurs after the first
interrupt handler has finished but before the interrupt is unmasked the
INT_STATUS register will be re-asserted and the interrupt handler runs a second
time once the interrupt is unmasked. This means with this flow handling strategy
no interrupts are lost for edge triggered interrupts. The handle_level_irq()
handler will have the desired behavior. (Note: The handle_edge_irq() only needs
to be used for edge triggered interrupts where the controller stops detecting
the interrupt event when the interrupt is masked, for this controller the
detection logic still works, while only the propagation is delayed when the
interrupt is masked.)

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2014-08-17 09:12:35 -05:00
Lars-Peter Clausen 190dc2e684 gpio: zynq: Clear pending interrupt when enabling a IRQ
The Zynq GPIO controller does not disable the interrupt detection when the
interrupt is masked and only disables the propagation of the interrupt. This
means when the controller detects an interrupt condition while the interrupt is
logically disabled (and masked) it will propagate the recorded interrupt event
once the interrupt is enabled. This will cause the interrupt consumer to see
spurious interrupts to prevent this first make sure that the interrupt is not
asserted and then enable it.

E.g. when a interrupt is requested with request_irq() it will be configured
according to the requested type (edge/level triggered, etc.) after that it will
be enabled. But the detection circuit might have already registered a false
interrupt before the interrupt type was correctly configured and once the
interrupt is unmasked this false interrupt will be propagated and the interrupt
handler for the just request interrupt will called.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2014-07-23 16:29:14 +02:00
Harini Katakam 3242ba117e gpio: Add driver for Zynq GPIO controller
Add support for GPIO controller used by Xilinx Zynq.

v3 changes:
 - Use linux/gpio/driver.h instead of linux/gpio.h
 - Make irq a local variable in probe

v2 changes:
 - convert to pm_runtime_force_(suspend|resume)
 - add pm_runtime_set_active in probe()
 - also (un)prepare clocks when they are dis-/enabled
 - add some missing calls to pm_runtime_get()
 - use pm_runtime_put() instead of sync variant
 - remove gpio chip in driver remove()
 - remove redundant type casts
 - directly use IO helpers
 - use BIT macro to set/clear bits
 - migrate to GPIOLIB_IRQCHIP

Signed-off-by: Harini Katakam <harinik@xilinx.com>
Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2014-07-10 11:11:41 +02:00