[PATCH/RFC] gpiolib: create irq_domain hierarchy bottom if possible

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Currently, gpiolib creates irq_domain compatible with hierarchical API
only when interrupts provided by gpiochip lay on top of existing
hierarchy. Otherwise, legacy API is used.

With this patch, as soon as
- irq_domain hierarchical API is enabled in the kernel config,
- chip driver does not request preallocated interrupt numbers,
- chip driver does not provide it's own irq_domain_ops,
- chip driver provides fwnode, either explicitly or via it's struct
  device,
irq_domain created by gpiolib will use hierarchical API even without
parent.

This allows other irqchips to lay on top of this irq_domain.
Example target use case is irq_inverter [1].

[1] https://lore.kernel.org/lkml/87fsqbznc2.wl-maz@xxxxxxxxxx/

Signed-off-by: Nikita Yushchenko <nikita.yoush@xxxxxxxxxxxxxxxxxx>
---
 drivers/gpio/gpiolib.c | 99 +++++++++++++++++++++++++-----------------
 1 file changed, 60 insertions(+), 39 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index abfbf546d159..db8eee07a8d7 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1095,14 +1095,6 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
 
 	chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq,  hwirq);
 
-	ret = girq->child_to_parent_hwirq(gc, hwirq, type,
-					  &parent_hwirq, &parent_type);
-	if (ret) {
-		chip_err(gc, "can't look up hwirq %lu\n", hwirq);
-		return ret;
-	}
-	chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
-
 	/*
 	 * We set handle_bad_irq because the .set_type() should
 	 * always be invoked and set the right type of handler.
@@ -1116,27 +1108,40 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
 			    NULL, NULL);
 	irq_set_probe(irq);
 
-	/* This parent only handles asserted level IRQs */
-	parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type);
-	if (!parent_arg)
-		return -ENOMEM;
-
-	chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
-		  irq, parent_hwirq);
 	irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
-	ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg);
-	/*
-	 * If the parent irqdomain is msi, the interrupts have already
-	 * been allocated, so the EEXIST is good.
-	 */
-	if (irq_domain_is_msi(d->parent) && (ret == -EEXIST))
-		ret = 0;
-	if (ret)
-		chip_err(gc,
-			 "failed to allocate parent hwirq %d for hwirq %lu\n",
-			 parent_hwirq, hwirq);
 
-	kfree(parent_arg);
+	if (d->parent) {
+		ret = girq->child_to_parent_hwirq(gc, hwirq, type,
+						  &parent_hwirq, &parent_type);
+		if (ret) {
+			chip_err(gc, "can't look up hwirq %lu\n", hwirq);
+			return ret;
+		}
+		chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
+
+		/* This parent only handles asserted level IRQs */
+		parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq,
+							     parent_type);
+		if (!parent_arg)
+			return -ENOMEM;
+
+		chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
+			  irq, parent_hwirq);
+		ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg);
+		/*
+		 * If the parent irqdomain is msi, the interrupts have already
+		 * been allocated, so the EEXIST is good.
+		 */
+		if (irq_domain_is_msi(d->parent) && (ret == -EEXIST))
+			ret = 0;
+		if (ret)
+			chip_err(gc,
+				 "failed to allocate parent hwirq %d for hwirq %lu\n",
+				 parent_hwirq, hwirq);
+
+		kfree(parent_arg);
+	}
+
 	return ret;
 }
 
@@ -1164,8 +1169,8 @@ static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops)
 
 static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
 {
-	if (!gc->irq.child_to_parent_hwirq ||
-	    !gc->irq.fwnode) {
+	if (gc->irq.parent_domain &&
+	    (!gc->irq.child_to_parent_hwirq || !gc->irq.fwnode)) {
 		chip_err(gc, "missing irqdomain vital data\n");
 		return -EINVAL;
 	}
@@ -1179,25 +1184,41 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
 
 	gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops);
 
-	gc->irq.domain = irq_domain_create_hierarchy(
-		gc->irq.parent_domain,
-		0,
-		gc->ngpio,
-		gc->irq.fwnode,
-		&gc->irq.child_irq_domain_ops,
-		gc);
+	if (gc->irq.parent_domain) {
+		gc->irq.domain = irq_domain_create_hierarchy(
+			gc->irq.parent_domain,
+			0,
+			gc->ngpio,
+			gc->irq.fwnode,
+			&gc->irq.child_irq_domain_ops,
+			gc);
+
+		if (gc->irq.domain)
+			gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip);
+	} else {
+		gc->irq.domain = irq_domain_create_linear(
+			gc->irq.fwnode ?: dev_fwnode(&gc->gpiodev->dev),
+			gc->ngpio,
+			&gc->irq.child_irq_domain_ops,
+			gc);
+	}
 
 	if (!gc->irq.domain)
 		return -ENOMEM;
 
-	gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip);
-
 	return 0;
 }
 
 static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
 {
-	return !!gc->irq.parent_domain;
+	if (gc->irq.parent_domain)
+		return true;	/* will add to existing hierarchy */
+
+	if (!gc->irq.first && !gc->irq.domain_ops &&
+	    (gc->irq.fwnode || dev_fwnode(&gc->gpiodev->dev)))
+		return true;	/* will create hierarchy bottom */
+
+	return false;
 }
 
 void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
-- 
2.30.2




[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux