[PATCH 4/4] irqchip: imgpdc: Pass on peripheral mask/unmasks to the parent

From: Ed Blake
Date: Mon Oct 02 2017 - 05:56:28 EST


Pass on peripheral (RTC/IR/WD) irq masks and unmasks to the parent
interrupt controller, as well as setting / clearing the relevant bits
in the IRQ_ROUTE register.

Clearing bits in the IRQ_ROUTE register will prevent future interrupts
from being passed on to the parent, but won't mask an existing
interrupt which has already made it to the parent. This is currently
causing peipheral interrupts to fire continuously when the system wakes
from a suspended state when one of the peripherals is used to wake the
system (e.g. RTC, IR). The interrupt occurs early in the wake process
(still in the noirq phase) and because the peripheral interrupt is
disabled at that point, the core marks it as pending and masks it out.
This mask must be passed to the parent controller to be effective.

Signed-off-by: Ed Blake <ed.blake@xxxxxxxxxxx>
---
drivers/irqchip/irq-imgpdc.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c
index d1bcfef..05c48dd 100644
--- a/drivers/irqchip/irq-imgpdc.c
+++ b/drivers/irqchip/irq-imgpdc.c
@@ -141,21 +141,31 @@ static struct pdc_intc_priv *irqd_to_priv(struct irq_data *data)
static void perip_irq_mask(struct irq_data *data)
{
struct pdc_intc_priv *priv = irqd_to_priv(data);
+ unsigned int parent_irq = priv->perip_irqs[data->hwirq];
+ struct irq_data *parent_irq_data = irq_get_irq_data(parent_irq);

raw_spin_lock(&priv->lock);
priv->irq_route &= ~data->mask;
pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
raw_spin_unlock(&priv->lock);
+
+ /* Pass on the mask to the parent */
+ parent_irq_data->chip->irq_mask(parent_irq_data);
}

static void perip_irq_unmask(struct irq_data *data)
{
struct pdc_intc_priv *priv = irqd_to_priv(data);
+ unsigned int parent_irq = priv->perip_irqs[data->hwirq];
+ struct irq_data *parent_irq_data = irq_get_irq_data(parent_irq);

raw_spin_lock(&priv->lock);
priv->irq_route |= data->mask;
pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
raw_spin_unlock(&priv->lock);
+
+ /* Pass on the unmask to the parent */
+ parent_irq_data->chip->irq_unmask(parent_irq_data);
}

static int syswake_irq_set_type(struct irq_data *data, unsigned int flow_type)
--
1.9.1