@@ -112,6 +112,30 @@ static unsigned int altera_gpio_irq_startup(struct irq_data *d)
return 0;
}
+static int altera_gpio_irq_reqres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ if (gpiochip_lock_as_irq(chip, d->hwirq)) {
+ dev_err(chip->parent, "unable to lock HW IRQ %lu for IRQ\n",
+ d->hwirq);
+ module_put(THIS_MODULE);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void altera_gpio_irq_relres(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ gpiochip_unlock_as_irq(chip, d->hwirq);
+ module_put(THIS_MODULE);
+}
+
static struct irq_chip altera_irq_chip = {
.name = "altera-gpio",
.irq_mask = altera_gpio_irq_mask,
@@ -119,6 +143,8 @@ static struct irq_chip altera_irq_chip = {
.irq_set_type = altera_gpio_irq_set_type,
.irq_startup = altera_gpio_irq_startup,
.irq_shutdown = altera_gpio_irq_mask,
+ .irq_request_resources = altera_gpio_irq_reqres,
+ .irq_release_resources = altera_gpio_irq_relres,
};
static int altera_gpio_get(struct gpio_chip *gc, unsigned offset)
@@ -252,6 +278,64 @@ static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
+static int altera_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct gpio_chip *chip = d->host_data;
+
+ irq_set_chip_data(irq, chip);
+ /*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+ irq_set_lockdep_class(irq, chip->lock_key);
+ irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static void altera_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops altera_gpio_domain_ops = {
+ .map = altera_gpio_irq_map,
+ .unmap = altera_gpio_irq_unmap,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int altera_gpiochip_irqchip_add(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip)
+{
+ static struct lock_class_key key;
+
+ if (!gpiochip || !irqchip)
+ return -EINVAL;
+
+ if (!gpiochip->parent) {
+ pr_err("missing gpiochip .dev parent pointer\n");
+ return -EINVAL;
+ }
+ gpiochip->irq_nested = false;
+ gpiochip->irqchip = irqchip;
+ gpiochip->irq_handler = handle_bad_irq;
+ gpiochip->irq_default_type = IRQ_TYPE_NONE;
+ gpiochip->to_irq = NULL;
+ gpiochip->lock_key = &key;
+ gpiochip->irqdomain = irq_domain_add_simple(
+ gpiochip->parent->of_node, gpiochip->ngpio, 0,
+ &altera_gpio_domain_ops, gpiochip);
+ if (!gpiochip->irqdomain) {
+ gpiochip->irqchip = NULL;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int altera_gpio_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -305,8 +389,8 @@ static int altera_gpio_probe(struct platform_device *pdev)
}
altera_gc->interrupt_trigger = reg;
- ret = gpiochip_irqchip_add(&altera_gc->mmchip.gc, &altera_irq_chip, 0,
- handle_bad_irq, IRQ_TYPE_NONE);
+ ret = altera_gpiochip_irqchip_add(&altera_gc->mmchip.gc,
+ &altera_irq_chip);
if (ret) {
dev_err(&pdev->dev, "could not add irqchip\n");
As documented in the device tree binding, #interrupt-cells should be 1 for this driver because the interrupt type is fixed in the hardware. When the device tree is configured as documented, an interrupt would trigger kernel/irq/irqdomain.c: WARN_ON(intsize < 2). The driver was attempting to use gpiochip_irqchip_add(), which uses gpiochip_irqchip_add_key() and is documented to work only with two celled IRQs. This appears to have been broken in the v7 -> v8 transition of the original patch adding the driver. Signed-off-by: Chris Lesiak <chris.lesiak@licor.com> --- drivers/gpio/gpio-altera.c | 88 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-)