On Mon, Jun 24, 2019 at 10:25 PM Linus Walleij <linus.walleij@xxxxxxxxxx> wrote: > > Hierarchical IRQ domains can be used to stack different IRQ > controllers on top of each other. > > Bring hierarchical IRQ domains into the GPIOLIB core with the > following basic idea: > > Drivers that need their interrupts handled hierarchically > specify a callback to translate the child hardware IRQ and > IRQ type for each GPIO offset to a parent hardware IRQ and > parent hardware IRQ type. > > Users have to pass the callback, fwnode, and parent irqdomain > before calling gpiochip_irqchip_add(). > > We use the new method of just filling in the struct > gpio_irq_chip before adding the gpiochip for all hierarchical > irqchips of this type. > > The code path for device tree is pretty straight-forward, > while the code path for old boardfiles or anything else will > be more convoluted requireing upfront allocation of the > interrupts when adding the chip. > > One specific use-case where this can be useful is if a power > management controller has top-level controls for wakeup > interrupts. In such cases, the power management controller can > be a parent to other interrupt controllers and program > additional registers when an IRQ has its wake capability > enabled or disabled. > > The hierarchical irqchip helper code will only be available > when IRQ_DOMAIN_HIERARCHY is selected to GPIO chips using > this should select or depend on that symbol. When using > hierarchical IRQs, the parent interrupt controller must > also be hierarchical all the way up to the top interrupt > controller wireing directly into the CPU, so on systems > that do not have this we can get rid of all the extra > code for supporting hierarchical irqs. > > Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> > Cc: Marc Zyngier <marc.zyngier@xxxxxxx> > Cc: Lina Iyer <ilina@xxxxxxxxxxxxxx> > Cc: Jon Hunter <jonathanh@xxxxxxxxxx> > Cc: Sowjanya Komatineni <skomatineni@xxxxxxxxxx> > Cc: Bitan Biswas <bbiswas@xxxxxxxxxx> > Cc: linux-tegra@xxxxxxxxxxxxxxx > Cc: David Daney <david.daney@xxxxxxxxxx> > Cc: Masahiro Yamada <yamada.masahiro@xxxxxxxxxxxxx> > Cc: Brian Masney <masneyb@xxxxxxxxxxxxx> > Signed-off-by: Thierry Reding <treding@xxxxxxxxxx> > Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx> > --- > ChangeLog RFC->v1: > - Tested on real hardware > - Incorporate Thierry's idea to have a translation callback. > He was right about this approach, I was wrong in insisting > on IRQ maps. > --- > Documentation/driver-api/gpio/driver.rst | 120 ++++++++-- > drivers/gpio/gpiolib.c | 285 ++++++++++++++++++++++- > include/linux/gpio/driver.h | 46 ++++ > 3 files changed, 426 insertions(+), 25 deletions(-) > > diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst > index 1ce7fcd0f989..3099c7fbefdb 100644 > --- a/Documentation/driver-api/gpio/driver.rst > +++ b/Documentation/driver-api/gpio/driver.rst > @@ -259,7 +259,7 @@ most often cascaded off a parent interrupt controller, and in some special > cases the GPIO logic is melded with a SoC's primary interrupt controller. > > The IRQ portions of the GPIO block are implemented using an irq_chip, using > -the header <linux/irq.h>. So basically such a driver is utilizing two sub- > +the header <linux/irq.h>. So this combined driver is utilizing two sub- > systems simultaneously: gpio and irq. > > It is legal for any IRQ consumer to request an IRQ from any irqchip even if it > @@ -391,14 +391,108 @@ Infrastructure helpers for GPIO irqchips > ---------------------------------------- > > To help out in handling the set-up and management of GPIO irqchips and the > -associated irqdomain and resource allocation callbacks, the gpiolib has > -some helpers that can be enabled by selecting the GPIOLIB_IRQCHIP Kconfig > -symbol: > - > -- gpiochip_irqchip_add(): adds a chained cascaded irqchip to a gpiochip. It > - will pass the struct gpio_chip* for the chip to all IRQ callbacks, so the > - callbacks need to embed the gpio_chip in its state container and obtain a > - pointer to the container using container_of(). > +associated irqdomain and resource allocation callbacks. These are activated > +by selecting the Kconfig symbol GPIOLIB_IRQCHIP. If the symbol > +IRQ_DOMAIN_HIERARCHY is also selected, hierarchical helpers will also be > +provided. A big portion of overhead code will be managed by gpiolib, > +under the assumption that your interrupts are 1-to-1-mapped to the > +GPIO line index: > + > + GPIO line offset Hardware IRQ > + 0 0 > + 1 1 > + 2 2 > + ... ... > + ngpio-1 ngpio-1 > + > +If some GPIO lines do not have corresponding IRQs, the bitmask valid_mask > +and the flag need_valid_mask in gpio_irq_chip can be used to mask off some > +lines as invalid for associating with IRQs. > + > +The preferred way to set up the helpers is to fill in the > +struct gpio_irq_chip inside struct gpio_chip before adding the gpio_chip. > +If you do this, the additional irq_chip will be set up by gpiolib at the > +same time as setting up the rest of the GPIO functionality. The following > +is a typical example of a cascaded interrupt handler using gpio_irq_chip: > + > + /* Typical state container with dynamic irqchip */ > + struct my_gpio { > + struct gpio_chip gc; > + struct irq_chip irq; > + }; > + > + int irq; /* from platform etc */ > + struct my_gpio *g; > + struct gpio_irq_chip *girq > + > + /* Set up the irqchip dynamically */ > + g->irq.name = "my_gpio_irq"; > + g->irq.irq_ack = my_gpio_ack_irq; > + g->irq.irq_mask = my_gpio_mask_irq; > + g->irq.irq_unmask = my_gpio_unmask_irq; > + g->irq.irq_set_type = my_gpio_set_irq_type; > + > + /* Get a pointer to the gpio_irq_chip */ > + girq = &g->gc.irq; > + girq->chip = &g->irq; > + girq->parent_handler = ftgpio_gpio_irq_handler; > + girq->num_parents = 1; > + girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), > + GFP_KERNEL); > + if (!girq->parents) > + return -ENOMEM; > + girq->default_type = IRQ_TYPE_NONE; > + girq->handler = handle_bad_irq; > + girq->parents[0] = irq; > + > + return devm_gpiochip_add_data(dev, &g->gc, g); > + > +The helper support using hierarchical interrupt controllers as well. > +In this case the typical set-up will look like this: > + > + /* Typical state container with dynamic irqchip */ > + struct my_gpio { > + struct gpio_chip gc; > + struct irq_chip irq; > + struct fwnode_handle *fwnode; > + }; > + > + int irq; /* from platform etc */ > + struct my_gpio *g; > + struct gpio_irq_chip *girq > + > + /* Set up the irqchip dynamically */ > + g->irq.name = "my_gpio_irq"; > + g->irq.irq_ack = my_gpio_ack_irq; > + g->irq.irq_mask = my_gpio_mask_irq; > + g->irq.irq_unmask = my_gpio_unmask_irq; > + g->irq.irq_set_type = my_gpio_set_irq_type; > + > + /* Get a pointer to the gpio_irq_chip */ > + girq = &g->gc.irq; > + girq->chip = &g->irq; > + girq->default_type = IRQ_TYPE_NONE; > + girq->handler = handle_bad_irq; > + girq->fwnode = g->fwnode; > + girq->parent_domain = parent; > + girq->child_to_parent_hwirq = my_gpio_child_to_parent_hwirq; > + > + return devm_gpiochip_add_data(dev, &g->gc, g); > + > +As you can see pretty similar, but you do not supply a parent handler for > +the IRQ, instead a parent irqdomain, an fwnode for the hardware and > +a funcion .child_to_parent_hwirq() that has the purpose of looking up > +the parent hardware irq from a child (i.e. this gpio chip) hardware irq. > +As always it is good to look at examples in the kernel tree for advice > +on how to find the required pieces. > + > +The old way of adding irqchips to gpiochips after registration is also still > +available but we try to move away from this: > + > +- DEPRECATED: gpiochip_irqchip_add(): adds a chained cascaded irqchip to a > + gpiochip. It will pass the struct gpio_chip* for the chip to all IRQ > + callbacks, so the callbacks need to embed the gpio_chip in its state > + container and obtain a pointer to the container using container_of(). > (See Documentation/driver-model/design-patterns.txt) > > - gpiochip_irqchip_add_nested(): adds a nested cascaded irqchip to a gpiochip, > @@ -406,10 +500,10 @@ symbol: > cascaded irq has to be handled by a threaded interrupt handler. > Apart from that it works exactly like the chained irqchip. > > -- gpiochip_set_chained_irqchip(): sets up a chained cascaded irq handler for a > - gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler > - data. Notice that we pass is as the handler data, since the irqchip data is > - likely used by the parent irqchip. > +- DEPRECATED: gpiochip_set_chained_irqchip(): sets up a chained cascaded irq > + handler for a gpio_chip from a parent IRQ and passes the struct gpio_chip* > + as handler data. Notice that we pass is as the handler data, since the > + irqchip data is likely used by the parent irqchip. > > - gpiochip_set_nested_irqchip(): sets up a nested cascaded irq handler for a > gpio_chip from a parent IRQ. As the parent IRQ has usually been > diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c > index e013d417a936..af72ffa02963 100644 > --- a/drivers/gpio/gpiolib.c > +++ b/drivers/gpio/gpiolib.c > @@ -1718,6 +1718,240 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, > } > EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); > > +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY > + > +/** > + * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip > + * to a gpiochip > + * @gc: the gpiochip to set the irqchip hierarchical handler to > + * @irqchip: the irqchip to handle this level of the hierarchy, the interrupt > + * will then percolate up to the parent > + */ > +static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc, > + struct irq_chip *irqchip) > +{ > + /* DT will deal with mapping each IRQ as we go along */ > + if (is_of_node(gc->irq.fwnode)) > + return; > + > + /* > + * This is for legacy and boardfile "irqchip" fwnodes: allocate > + * irqs upfront instead of dynamically since we don't have the > + * dynamic type of allocation that hardware description languages > + * provide. Once all GPIO drivers using board files are gone from > + * the kernel we can delete this code, but for a transitional period > + * it is necessary to keep this around. > + */ > + if (is_fwnode_irqchip(gc->irq.fwnode)) { > + int i; > + int ret; > + > + for (i = 0; i < gc->ngpio; i++) { > + struct irq_fwspec fwspec; > + unsigned int parent_hwirq; > + unsigned int parent_type; > + struct gpio_irq_chip *girq = &gc->irq; > + > + /* > + * We call the child to parent translation function > + * only to check if the child IRQ is valid or not. > + * Just pick the rising edge type here as that is what > + * we likely need to support. > + */ > + ret = girq->child_to_parent_hwirq(gc, i, > + IRQ_TYPE_EDGE_RISING, > + &parent_hwirq, > + &parent_type); > + if (ret) { > + chip_err(gc, "skip set-up on hwirq %d\n", > + i); > + continue; > + } > + > + fwspec.fwnode = gc->irq.fwnode; > + /* This is the hwirq for the GPIO line side of things */ > + fwspec.param[0] = i; > + /* Just pick something */ > + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; > + fwspec.param_count = 2; > + ret = __irq_domain_alloc_irqs(gc->irq.domain, > + /* just pick something */ > + -1, > + 1, > + NUMA_NO_NODE, > + &fwspec, > + false, > + NULL); > + if (ret < 0) { > + chip_err(gc, > + "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", > + i, parent_hwirq, > + ret); > + } > + } > + } > + > + chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); > + > + return; > +} > + > +static int gpiochip_hierarchy_irq_domain_translate(struct irq_domain *d, > + struct irq_fwspec *fwspec, > + unsigned long *hwirq, > + unsigned int *type) > +{ > + /* We support standard DT translation */ > + if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { > + return irq_domain_translate_twocell(d, fwspec, hwirq, type); > + } > + > + /* This is for board files and others not using DT */ > + if (is_fwnode_irqchip(fwspec->fwnode)) { > + int ret; > + > + ret = irq_domain_translate_twocell(d, fwspec, hwirq, type); > + if (ret) > + return ret; > + WARN_ON(*type == IRQ_TYPE_NONE); > + return 0; > + } > + return -EINVAL; > +} > + > +static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, > + unsigned int irq, > + unsigned int nr_irqs, > + void *data) > +{ > + struct gpio_chip *gc = d->host_data; > + irq_hw_number_t hwirq; > + unsigned int type = IRQ_TYPE_NONE; > + struct irq_fwspec *fwspec = data; > + int ret; > + int i; We always expect nr_irqs is 1. As gpio-uniphier.c, you can error out with WARN_ON if nr_irqs != 1 > + chip_info(gc, "called %s\n", __func__); Is this a debug message? > + > + ret = gpiochip_hierarchy_irq_domain_translate(d, fwspec, &hwirq, &type); > + if (ret) > + return ret; > + > + chip_info(gc, "allocate IRQ %d..%d, hwirq %lu..%lu\n", > + irq, irq + nr_irqs - 1, > + hwirq, hwirq + nr_irqs - 1); Ditto. > + > + for (i = 0; i < nr_irqs; i++) { This for-loop is unneeded because nr_irqs is 1. > + struct irq_fwspec parent_fwspec; > + unsigned int parent_hwirq; > + unsigned int parent_type; > + struct gpio_irq_chip *girq = &gc->irq; > + > + 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_info(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. > + */ > + irq_domain_set_info(d, > + irq + i, > + hwirq + i, > + gc->irq.chip, > + gc, > + handle_bad_irq, > + NULL, NULL); > + irq_set_probe(irq + i); > + > + /* > + * Create a IRQ fwspec to send up to the parent irqdomain: > + * specify the hwirq we address on the parent and tie it > + * 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; > + 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, > + &parent_fwspec); > + if (ret) > + chip_err(gc, > + "failed to allocate parent hwirq %d for hwirq %lu\n", > + parent_hwirq, hwirq); > + } > + > + 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 int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) > +{ > + if (!gc->irq.parent_domain) { > + chip_err(gc, "missing parent irqdomain\n"); > + return -EINVAL; > + } Is this check needed? gpiochip_hierarchy_is_hierarchical() has already checked this. > + if (!gc->irq.parent_domain || Redundant check second time. > + !gc->irq.child_to_parent_hwirq || > + !gc->irq.fwnode) { > + chip_err(gc, "missing irqdomain vital data\n"); > + return -EINVAL; > + } > + > + gc->irq.domain = irq_domain_create_hierarchy( > + gc->irq.parent_domain, > + IRQ_DOMAIN_FLAG_HIERARCHY, You do not need to pass IRQ_DOMAIN_FLAG_HIERARCHY. This is set by irq_domain_check_hierarchy(). > + gc->ngpio, > + gc->irq.fwnode, > + &gpiochip_hierarchy_domain_ops, > + gc); > + > + if (!gc->irq.domain) { > + chip_err(gc, "failed to add hierarchical domain\n"); > + return -EINVAL; This should be ... return -ENOMEM; because __irq_domain_add() fails when memory allocation fails. Obviously, this also means you should drop chip_err() above since we do not print error message for ENOMEM. > + } > + > + gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip); > + > + chip_info(gc, "set up hierarchical irqdomain\n"); I see so many chip_info(). I think they should be chip_dbg() or removed entirely. > + return 0; > +} > + > +static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) > +{ > + return !!gc->irq.parent_domain; > +} > + > +#else > + > +static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) > +{ > + return -EINVAL; > +} > + > +static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) > +{ > + return false; > +} > + > +#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ > + > /** > * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip > * @d: the irqdomain used by this irqchip > @@ -1786,6 +2020,11 @@ static const struct irq_domain_ops gpiochip_domain_ops = { > .xlate = irq_domain_xlate_twocell, > }; > > +/* > + * TODO: move these activate/deactivate in under the hierarchicial > + * irqchip implementation as static once SPMI and SSBI (all external > + * users) are phased over. > + */ > /** > * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ > * @domain: The IRQ domain used by this IRQ chip > @@ -1825,10 +2064,23 @@ EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); > > static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) > { > + struct irq_domain *domain = chip->irq.domain; > + > if (!gpiochip_irqchip_irq_valid(chip, offset)) > return -ENXIO; > > - return irq_create_mapping(chip->irq.domain, offset); > + if (irq_domain_is_hierarchy(domain)) { > + struct irq_fwspec spec; > + > + spec.fwnode = domain->fwnode; > + spec.param_count = 2; > + spec.param[0] = offset; > + spec.param[1] = IRQ_TYPE_NONE; > + > + return irq_create_fwspec_mapping(&spec); > + } > + > + return irq_create_mapping(domain, offset); > } > > static int gpiochip_irq_reqres(struct irq_data *d) > @@ -1905,7 +2157,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, > struct lock_class_key *request_key) > { > struct irq_chip *irqchip = gpiochip->irq.chip; > - const struct irq_domain_ops *ops; > + const struct irq_domain_ops *ops = NULL; > struct device_node *np; > unsigned int type; > unsigned int i; > @@ -1941,16 +2193,25 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, > gpiochip->irq.lock_key = lock_key; > gpiochip->irq.request_key = request_key; > > - if (gpiochip->irq.domain_ops) > - ops = gpiochip->irq.domain_ops; > - else > - ops = &gpiochip_domain_ops; > - > - gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio, > - gpiochip->irq.first, > - ops, gpiochip); > - if (!gpiochip->irq.domain) > - return -EINVAL; > + /* If a parent irqdomain is provided, let's build a hierarchy */ > + if (gpiochip_hierarchy_is_hierarchical(gpiochip)) { > + int ret = gpiochip_hierarchy_add_domain(gpiochip); > + if (ret) > + return ret; > + } else { > + /* Some drivers provide custom irqdomain ops */ > + if (gpiochip->irq.domain_ops) > + ops = gpiochip->irq.domain_ops; > + > + if (!ops) > + ops = &gpiochip_domain_ops; > + gpiochip->irq.domain = irq_domain_add_simple(np, > + gpiochip->ngpio, > + gpiochip->irq.first, > + ops, gpiochip); > + if (!gpiochip->irq.domain) > + return -EINVAL; > + } > > if (gpiochip->irq.parent_handler) { > void *data = gpiochip->irq.parent_handler_data ?: gpiochip; > diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h > index a1d273c96016..e32d02cb2d08 100644 > --- a/include/linux/gpio/driver.h > +++ b/include/linux/gpio/driver.h > @@ -22,6 +22,9 @@ enum gpiod_flags; > #ifdef CONFIG_GPIOLIB > > #ifdef CONFIG_GPIOLIB_IRQCHIP > + > +struct gpio_chip; > + > /** > * struct gpio_irq_chip - GPIO interrupt controller > */ > @@ -48,6 +51,49 @@ struct gpio_irq_chip { > */ > const struct irq_domain_ops *domain_ops; > > +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY > + /** > + * @fwnode: > + * > + * Firmware node corresponding to this gpiochip/irqchip, necessary > + * for hierarchical irqdomain support. > + */ > + struct fwnode_handle *fwnode; > + > + /** > + * @parent_domain: > + * > + * If non-NULL, will be set as the parent of this GPIO interrupt > + * controller's IRQ domain to establish a hierarchical interrupt > + * domain. The presence of this will activate the hierarchical > + * interrupt support. > + */ > + struct irq_domain *parent_domain; > + > + /** > + * @child_to_parent_hwirq: > + * > + * This callback translates a child hardware IRQ offset to a parent > + * hardware IRQ offset on a hierarchical interrupt chip. The child > + * hardware IRQs correspond to the GPIO index 0..ngpio-1 (see the > + * ngpio field of struct gpio_chip) and the corresponding parent > + * hardware IRQ and type (such as IRQ_TYPE_*) shall be returned by > + * the driver. The driver can calculate this from an offset or using > + * a lookup table or whatever method is best for this chip. Return > + * 0 on successful translation in the driver. > + * > + * If some ranges of hardware IRQs do not have a corresponding parent > + * HWIRQ, return -EINVAL, but also make sure to fill in @valid_mask and > + * @need_valid_mask to make these GPIO lines unavailable for > + * translation. > + */ > + int (*child_to_parent_hwirq)(struct gpio_chip *chip, > + unsigned int child_hwirq, > + unsigned int child_type, > + unsigned int *parent_hwirq, > + unsigned int *parent_type); > +#endif > + > /** > * @handler: > * > -- > 2.21.0 > -- Best Regards Masahiro Yamada