[PATCH 2/4] gpio: allow customizing hierarchical IRQ chips

From: Brian Masney
Date: Mon Jul 08 2019 - 07:02:13 EST


Now that the GPIO core has support for hierarchical IRQ chips, let's add
support for three new callbacks in struct gpio_irq_chip:

populate_parent_fwspec:
This optional callback populates the struct irq_fwspec for the
parent's IRQ domain. If this is not specified, then
gpiochip_populate_parent_fwspec_twocell will be used. A four-cell
variant named &gpiochip_populate_parent_fwspec_twocell is also
available.

child_pin_to_irq:
This optional callback is used to translate the child's GPIO pin
number to an IRQ number for the GPIO to_irq() callback. If this is
not specified, then a default callback will be provided that
returns the pin number.

child_irq_domain_ops:
The IRQ domain operations that will be used for this GPIO IRQ
chip. If no operations are provided, then default callbacks will
be populated to setup the IRQ hierarchy. Some drivers need to
supply their own translate function.

These will be initially used by Qualcomm's spmi-gpio and ssbi-gpio.

Signed-off-by: Brian Masney <masneyb@xxxxxxxxxxxxx>
---
Note: checkpatch doesn't like that child_irq_domain_ops is not const.

drivers/gpio/gpiolib.c | 52 +++++++++++++++++++++++++++----------
include/linux/gpio/driver.h | 35 +++++++++++++++++++++++++
2 files changed, 73 insertions(+), 14 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 06c9cf714c99..5423242deb81 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1778,7 +1778,7 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,

fwspec.fwnode = gc->irq.fwnode;
/* This is the hwirq for the GPIO line side of things */
- fwspec.param[0] = i;
+ fwspec.param[0] = girq->child_pin_to_irq(gc, i);
/* Just pick something */
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
fwspec.param_count = 2;
@@ -1841,7 +1841,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,

chip_info(gc, "called %s\n", __func__);

- ret = gpiochip_hierarchy_irq_domain_translate(d, fwspec, &hwirq, &type);
+ ret = gc->irq.child_irq_domain_ops.translate(d, fwspec, &hwirq, &type);
if (ret)
return ret;

@@ -1882,10 +1882,9 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
* all together up the chain.
*/
parent_fwspec.fwnode = d->parent->fwnode;
- parent_fwspec.param_count = 2;
- parent_fwspec.param[0] = parent_hwirq;
/* This parent only handles asserted level IRQs */
- parent_fwspec.param[1] = parent_type;
+ girq->populate_parent_fwspec(gc, &parent_fwspec, parent_hwirq,
+ parent_type);
chip_info(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
irq + i, parent_hwirq);
ret = irq_domain_alloc_irqs_parent(d, irq + i, 1,
@@ -1899,13 +1898,29 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
return 0;
}

-static const struct irq_domain_ops gpiochip_hierarchy_domain_ops = {
- .activate = gpiochip_irq_domain_activate,
- .deactivate = gpiochip_irq_domain_deactivate,
- .translate = gpiochip_hierarchy_irq_domain_translate,
- .alloc = gpiochip_hierarchy_irq_domain_alloc,
- .free = irq_domain_free_irqs_common,
-};
+static unsigned int gpiochip_child_pin_to_irq_noop(struct gpio_chip *chip,
+ unsigned int pin)
+{
+ return pin;
+}
+
+static void gpiochip_add_default_irq_domain_ops(struct irq_domain_ops *ops)
+{
+ if (!ops->activate)
+ ops->activate = gpiochip_irq_domain_activate;
+
+ if (!ops->deactivate)
+ ops->deactivate = gpiochip_irq_domain_deactivate;
+
+ if (!ops->translate)
+ ops->translate = gpiochip_hierarchy_irq_domain_translate;
+
+ if (!ops->alloc)
+ ops->alloc = gpiochip_hierarchy_irq_domain_alloc;
+
+ if (!ops->free)
+ ops->free = irq_domain_free_irqs_common;
+}

static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
{
@@ -1921,12 +1936,21 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
return -EINVAL;
}

+ if (!gc->irq.child_pin_to_irq)
+ gc->irq.child_pin_to_irq = gpiochip_child_pin_to_irq_noop;
+
+ if (!gc->irq.populate_parent_fwspec)
+ gc->irq.populate_parent_fwspec =
+ gpiochip_populate_parent_fwspec_twocell;
+
+ gpiochip_add_default_irq_domain_ops(&gc->irq.child_irq_domain_ops);
+
gc->irq.domain = irq_domain_create_hierarchy(
gc->irq.parent_domain,
IRQ_DOMAIN_FLAG_HIERARCHY,
gc->ngpio,
gc->irq.fwnode,
- &gpiochip_hierarchy_domain_ops,
+ &gc->irq.child_irq_domain_ops,
gc);

if (!gc->irq.domain) {
@@ -2106,7 +2130,7 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)

spec.fwnode = domain->fwnode;
spec.param_count = 2;
- spec.param[0] = offset;
+ spec.param[0] = chip->irq.child_pin_to_irq(chip, offset);
spec.param[1] = IRQ_TYPE_NONE;

return irq_create_fwspec_mapping(&spec);
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index 6b6bca20c8f9..fb9c126dd8f0 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -93,6 +93,41 @@ struct gpio_irq_chip {
unsigned int child_type,
unsigned int *parent_hwirq,
unsigned int *parent_type);
+
+ /**
+ * @populate_parent_fwspec:
+ *
+ * This optional callback populates the &struct irq_fwspec for the
+ * parent's IRQ domain. If this is not specified, then
+ * &gpiochip_populate_parent_fwspec_twocell will be used. A four-cell
+ * variant named &gpiochip_populate_parent_fwspec_twocell is also
+ * available.
+ */
+ void (*populate_parent_fwspec)(struct gpio_chip *chip,
+ struct irq_fwspec *fwspec,
+ unsigned int parent_hwirq,
+ unsigned int parent_type);
+
+ /**
+ * @child_pin_to_irq:
+ *
+ * This optional callback is used to translate the child's GPIO pin
+ * number to an IRQ number for the GPIO to_irq() callback. If this is
+ * not specified, then a default callback will be provided that
+ * returns the pin number.
+ */
+ unsigned int (*child_pin_to_irq)(struct gpio_chip *chip,
+ unsigned int pin);
+
+ /**
+ * @child_irq_domain_ops:
+ *
+ * The IRQ domain operations that will be used for this GPIO IRQ
+ * chip. If no operations are provided, then default callbacks will
+ * be populated to setup the IRQ hierarchy. Some drivers need to
+ * supply their own translate function.
+ */
+ struct irq_domain_ops child_irq_domain_ops;
#endif

/**
--
2.20.1