[PATCH 3/3] genirq: Check for trigger type mismatch in __setup_irq()

Manivannan Sadhasivam
2022-05-30T08:09:19Z

Currently, if the trigger type defined by the platform like DT does not
match the driver requested trigger type, the below warning is shown
during platform_get_irq() but only during the second time of the drive
probe (due to probe deferral or module unload/load).

irq: type mismatch, failed to map hwirq-9 for interrupt-controller@b220000!

Consider a typical usecase of requesting an IRQ in a driver:

```
/* Assume DT has set the trigger type to IRQF_TYPE_LEVEL_HIGH */

q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog");
if (q6v5->wdog_irq <= 0)
return q6v5->wdog_irq;

ret = devm_request_threaded_irq(&pdev->dev, q6v5->wdog_irq,
NULL, q6v5_wdog_interrupt,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"q6v5 wdog", q6v5);
if (ret) {
dev_err(&pdev->dev, "failed to acquire wdog IRQ\n");
return ret;
}
```

For the first time probe of a driver, platform_get_irq_byname() does not
return an error and it sets the platform requested trigger type. Then,
request_irq() also does not check for the trigger type mismatch and sets
the driver requested trigger type. Later if the driver gets probed again,
platform_get_irq() throws the "type mismatch" warning and fails.

Ideally, request_irq() should throw the error during the first time itself,
when it detects the trigger type mismatch. So let's add a check in
__setup_irq() for checking the trigger type mismatch.

It should be noted that the platform trigger type could be IRQ_TYPE_NONE
in some cases like IRQ controller inside the GPIOCHIP. For those cases,
the check should be skipped.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
kernel/irq/manage.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index c03f71d5ec10..dd28c4944172 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1480,8 +1480,18 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
* If the trigger type is not specified by the caller,
* then use the default for this interrupt.
*/
- if (!(new->flags & IRQF_TRIGGER_MASK))
- new->flags |= irqd_get_trigger_type(&desc->irq_data);
+ flags = irqd_get_trigger_type(&desc->irq_data);
+ if (!(new->flags & IRQF_TRIGGER_MASK)) {
+ new->flags |= flags;
+ } else if (flags && ((new->flags & IRQF_TRIGGER_MASK) != flags)) {
+ /*
+ * Bail out if the default trigger is not IRQ_TYPE_NONE and the
+ * caller specified trigger does not match the default trigger type.
+ */
+ pr_err("Trigger type %u does not match default type %lu for %s (irq %d)\n",
+ new->flags & IRQF_TRIGGER_MASK, flags, new->name, irq);
+ return -EINVAL;
+ }

/*
* Check whether the interrupt nests into another interrupt
--
2.25.1