| From 4ecdf784d8d66876d87e977db9aca755a5418a54 Mon Sep 17 00:00:00 2001 |
| From: Thierry Reding <treding@nvidia.com> |
| Date: Tue, 1 Oct 2019 13:48:29 +0200 |
| Subject: [PATCH] soc/tegra: pmc: Fix crashes for hierarchical interrupts |
| |
| commit c9e753767a9c75d2044fb7343950a6a992d34a16 upstream. |
| |
| Interrupts that don't have an associated wake event or GPIO wake events |
| end up with an associate IRQ chip that is NULL and which causes IRQ code |
| to crash. This is because we don't implicitly set the parent IRQ chip by |
| allocating the interrupt at the parent. However, there really isn't a |
| corresponding interrupt at the parent, so we need to work around this by |
| setting the special no_irq_chip as the IRQ chip for these interrupts. |
| |
| Fixes: 19906e6b1667 ("soc/tegra: pmc: Add wake event support") |
| Signed-off-by: Thierry Reding <treding@nvidia.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c |
| index 8878720dd779..ff8a2a6258d5 100644 |
| --- a/drivers/soc/tegra/pmc.c |
| +++ b/drivers/soc/tegra/pmc.c |
| @@ -1890,14 +1890,40 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq, |
| event->id, |
| &pmc->irq, pmc); |
| |
| + /* |
| + * GPIOs don't have an equivalent interrupt in the |
| + * parent controller (GIC). However some code, such |
| + * as the one in irq_get_irqchip_state(), require a |
| + * valid IRQ chip to be set. Make sure that's the |
| + * case by passing NULL here, which will install a |
| + * dummy IRQ chip for the interrupt in the parent |
| + * domain. |
| + */ |
| + if (domain->parent) |
| + irq_domain_set_hwirq_and_chip(domain->parent, |
| + virq, 0, NULL, |
| + NULL); |
| + |
| break; |
| } |
| } |
| |
| - if (i == soc->num_wake_events) |
| + if (i == soc->num_wake_events) { |
| err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX, |
| &pmc->irq, pmc); |
| |
| + /* |
| + * Interrupts without a wake event don't have a corresponding |
| + * interrupt in the parent controller (GIC). Pass NULL for the |
| + * chip here, which causes a dummy IRQ chip to be installed |
| + * for the interrupt in the parent domain, to make this |
| + * explicit. |
| + */ |
| + if (domain->parent) |
| + irq_domain_set_hwirq_and_chip(domain->parent, virq, 0, |
| + NULL, NULL); |
| + } |
| + |
| return err; |
| } |
| |
| -- |
| 2.7.4 |
| |