On Tue, Mar 25, 2014 at 9:51 PM, Linus Walleij <linus.walleij@xxxxxxxxxx> wrote: > This provides a function gpiochip_irqchip_add() to set > up an irqchip for a GPIO controller, and a function > gpiochip_set_chained_irqchip() to chain it to a parent > irqchip. > > Most GPIOs are of the type where a number of lines form > a cascaded interrupt controller chained onto > the primary system interrupt controller (or further down the > chain) so let's add this helper and factor the code to > request the lines to be used as IRQs, the .to_irq() function > and the irqdomain into the core as well. Acked-by: Alexandre Courbot <acourbot@xxxxxxxxxx> > Suggested-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx> > Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx> > --- > drivers/gpio/Kconfig | 3 + > drivers/gpio/gpiolib.c | 188 ++++++++++++++++++++++++++++++++++++++++++++ > include/linux/gpio/driver.h | 29 +++++++ > 3 files changed, 220 insertions(+) > > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig > index 2e461e459d88..51c782532687 100644 > --- a/drivers/gpio/Kconfig > +++ b/drivers/gpio/Kconfig > @@ -55,6 +55,9 @@ config GPIO_ACPI > def_bool y > depends on ACPI > > +config GPIOLIB_IRQCHIP > + bool > + > config DEBUG_GPIO > bool "Debug GPIO calls" > depends on DEBUG_KERNEL > diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c > index 584d2b465f84..f76c952e08b2 100644 > --- a/drivers/gpio/gpiolib.c > +++ b/drivers/gpio/gpiolib.c > @@ -1254,6 +1254,9 @@ fail: > } > EXPORT_SYMBOL_GPL(gpiochip_add); > > +/* Forward-declaration */ > +static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); > + > /** > * gpiochip_remove() - unregister a gpio_chip > * @chip: the chip to unregister > @@ -1270,6 +1273,7 @@ int gpiochip_remove(struct gpio_chip *chip) > > spin_lock_irqsave(&gpio_lock, flags); > > + gpiochip_irqchip_remove(chip); > gpiochip_remove_pin_ranges(chip); > of_gpiochip_remove(chip); > > @@ -1339,6 +1343,190 @@ static struct gpio_chip *find_chip_by_name(const char *name) > return gpiochip_find((void *)name, gpiochip_match_name); > } > > +#ifdef CONFIG_GPIOLIB_IRQCHIP > + > +/* > + * The following is irqchip helper code for gpiochips. > + */ > + > +/** > + * gpiochip_add_chained_irqchip() - adds a chained irqchip to a gpiochip > + * @gpiochip: the gpiochip to add the irqchip to > + * @irqchip: the irqchip to add to the gpiochip > + * @parent_irq: the irq number corresponding to the parent IRQ for this > + * chained irqchip > + * @parent_handler: the parent interrupt handler for the accumulated IRQ > + * coming out of the gpiochip > + */ > +void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, > + struct irq_chip *irqchip, > + int parent_irq, > + irq_flow_handler_t parent_handler) > +{ > + irq_set_chained_handler(parent_irq, parent_handler); > + /* > + * The parent irqchip is already using the chip_data for this > + * irqchip, so our callbacks simply use the handler_data. > + */ > + irq_set_handler_data(parent_irq, gpiochip); > +} > +EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip); > + > +/** > + * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip > + * @d: the irqdomain used by this irqchip > + * @irq: the global irq number used by this GPIO irqchip irq > + * @hwirq: the local IRQ/GPIO line offset on this gpiochip > + * > + * This function will set up the mapping for a certain IRQ line on a > + * gpiochip by assigning the gpiochip as chip data, and using the irqchip > + * stored inside the gpiochip. > + */ > +static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, > + irq_hw_number_t hwirq) > +{ > + struct gpio_chip *chip = d->host_data; > + > + irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler); > + irq_set_chip_data(irq, chip); > +#ifdef CONFIG_ARM > + set_irq_flags(irq, IRQF_VALID); > +#else > + irq_set_noprobe(irq); > +#endif > + irq_set_irq_type(irq, chip->irq_default_type); > + > + return 0; > +} > + > +static const struct irq_domain_ops gpiochip_domain_ops = { > + .map = gpiochip_irq_map, > + /* Virtually all GPIO irqchips are twocell:ed */ > + .xlate = irq_domain_xlate_twocell, > +}; > + > +static int gpiochip_irq_reqres(struct irq_data *d) > +{ > + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); > + > + if (gpio_lock_as_irq(chip, d->hwirq)) { > + chip_err(chip, > + "unable to lock HW IRQ %lu for IRQ\n", > + d->hwirq); > + return -EINVAL; > + } > + return 0; > +} > + > +static void gpiochip_irq_relres(struct irq_data *d) > +{ > + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); > + > + gpio_unlock_as_irq(chip, d->hwirq); > +} > + > +static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) > +{ > + return irq_find_mapping(chip->irqdomain, offset); > +} > + > +/** > + * gpiochip_irqchip_remove() - removes an irqchip added to a gpiochip > + * @gpiochip: the gpiochip to remove the irqchip from > + * > + * This is called only from gpiochip_remove() > + */ > +static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) > +{ > + if (gpiochip->irqdomain) > + irq_domain_remove(gpiochip->irqdomain); > + > + if (gpiochip->irqchip) { > + gpiochip->irqchip->irq_request_resources = NULL; > + gpiochip->irqchip->irq_release_resources = NULL; > + gpiochip->irqchip = NULL; > + } > +} > + > +/** > + * gpiochip_irqchip_add() - adds an irqchip to a gpiochip > + * @gpiochip: the gpiochip to add the irqchip to > + * @irqchip: the irqchip to add to the gpiochip > + * @first_irq: if not dynamically assigned, the base (first) IRQ to > + * allocate gpiochip irqs from > + * @handler: the irq handler to use (often a predefined irq core function) > + * @type: the default type for IRQs on this irqchip > + * > + * This function closely associates a certain irqchip with a certain > + * gpiochip, providing an irq domain to translate the local IRQs to > + * global irqs in the gpiolib core, and making sure that the gpiochip > + * is passed as chip data to all related functions. Driver callbacks > + * need to use container_of() to get their local state containers back > + * from the gpiochip passed as chip data. An irqdomain will be stored > + * in the gpiochip that shall be used by the driver to handle IRQ number > + * translation. The gpiochip will need to be initialized and registered > + * before calling this function. > + * > + * This function will handle two cell:ed simple IRQs. Everything else > + * need to be open coded. > + */ > +int gpiochip_irqchip_add(struct gpio_chip *gpiochip, > + struct irq_chip *irqchip, > + unsigned int first_irq, > + irq_flow_handler_t handler, > + unsigned int type) > +{ > + struct device_node *of_node; > + unsigned int offset; > + > + if (!gpiochip || !irqchip) > + return -EINVAL; > + > + if (!gpiochip->dev) { > + pr_err("missing gpiochip .dev parent pointer\n"); > + return -EINVAL; > + } > + of_node = gpiochip->dev->of_node; > +#if CONFIG_OF_GPIO > + /* > + * If the gpiochip has an assigned OF node this takes precendence > + * FIXME: get rid of this and use gpiochip->dev->of_node everywhere > + */ > + if (gpiochip->of_node) > + of_node = gpiochip->of_node; > +#endif > + gpiochip->irqchip = irqchip; > + gpiochip->irq_handler = handler; > + gpiochip->irq_default_type = type; > + gpiochip->to_irq = gpiochip_to_irq; > + gpiochip->irqdomain = irq_domain_add_simple(of_node, > + gpiochip->ngpio, first_irq, > + &gpiochip_domain_ops, gpiochip); > + if (!gpiochip->irqdomain) { > + gpiochip->irqchip = NULL; > + return -EINVAL; > + } > + irqchip->irq_request_resources = gpiochip_irq_reqres; > + irqchip->irq_release_resources = gpiochip_irq_relres; > + > + /* > + * Prepare the mapping since the irqchip shall be orthogonal to > + * to any gpiochip calls. If the first_irq was zero, this is If you ever need to send a v2 sometime, maybe fix the double "to" in this comment. ;) -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html