During the time window of noirq suspend and noirq resume, if a GPIO
pin is enabled as system wakeup source, the corresponding GPIO line's
IRQ will be unmasked, and GPIO bank will NOT lost power, when GPIO irq
arrives, generic irq handler will mask it until GPIO driver is ready
to handle it, but in GPIO noirq resume callback, current
implementation will restore the IMR register using the value saved in
previous noirq suspend callback which is unmasked, so this GPIO line
with IRQ pending will be unmasked again after GPIO IMR regitster is
restored, ARM will be triggered to handle this IRQ again, because of
the change of commit
bf22ff45bed6 ("genirq: Avoid unnecessary low
level irq function calls"), the mask_irq function will check the
status of irq_data to decide whether to mask the irq, in this
scenario, since the GPIO IRQ is being masked before GPIO noirq resume,
IRQD_IRQ_MASKED flag is set, so the second time GPIO IRQ triggered by
restoring GPIO IMR register, the irq_mask callback will be skipped and
cause ARM busy handling the GPIO IRQ come all the way and no response to
user anymore.
Signed-off-by: Bai Ping <ping.bai@nxp.com>
Reviewed-by: Anson Huang <Anson.Huang@nxp.com>
(cherry picked from commit
c03d9ec1f861ff7cd04637ef01aa7e14388277b6)
Signed-off-by: Arulpandiyan Vadivel <arulpandiyan_vadivel@mentor.com>
spin_lock_irqsave(&port->gc.bgpio_lock, flags);
port->suspend_saved_reg[0] = readl(port->base + GPIO_ICR1);
port->suspend_saved_reg[1] = readl(port->base + GPIO_ICR2);
- port->suspend_saved_reg[2] = readl(port->base + GPIO_IMR);
port->suspend_saved_reg[3] = readl(port->base + GPIO_GDIR);
port->suspend_saved_reg[4] = readl(port->base + GPIO_EDGE_SEL);
port->suspend_saved_reg[5] = readl(port->base + GPIO_DR);
spin_lock_irqsave(&port->gc.bgpio_lock, flags);
writel(port->suspend_saved_reg[0], port->base + GPIO_ICR1);
writel(port->suspend_saved_reg[1], port->base + GPIO_ICR2);
- writel(port->suspend_saved_reg[2], port->base + GPIO_IMR);
writel(port->suspend_saved_reg[3], port->base + GPIO_GDIR);
writel(port->suspend_saved_reg[4], port->base + GPIO_EDGE_SEL);
writel(port->suspend_saved_reg[5], port->base + GPIO_DR);