Loading Documentation/driver-api/gpio/driver.rst +142 −33 Original line number Diff line number Diff line Loading @@ -417,30 +417,66 @@ 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 chained cascaded interrupt handler using the gpio_irq_chip: the gpio_irq_chip. Note how the mask/unmask (or disable/enable) functions call into the core gpiolib code: .. code-block:: c /* Typical state container with dynamic irqchip */ /* Typical state container */ struct my_gpio { struct gpio_chip gc; struct irq_chip irq; }; static void my_gpio_mask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); /* * Perform any necessary action to mask the interrupt, * and then call into the core code to synchronise the * state. */ gpiochip_disable_irq(gc, d->hwirq); } static void my_gpio_unmask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); gpiochip_enable_irq(gc, d->hwirq); /* * Perform any necessary action to unmask the interrupt, * after having called into the core code to synchronise * the state. */ } /* * Statically populate the irqchip. Note that it is made const * (further indicated by the IRQCHIP_IMMUTABLE flag), and that * the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra * callbacks to the structure. */ static const struct irq_chip my_gpio_irq_chip = { .name = "my_gpio_irq", .irq_ack = my_gpio_ack_irq, .irq_mask = my_gpio_mask_irq, .irq_unmask = my_gpio_unmask_irq, .irq_set_type = my_gpio_set_irq_type, .flags = IRQCHIP_IMMUTABLE, /* Provide the gpio resource callbacks */ GPIOCHIP_IRQ_RESOURCE_HELPERS, }; 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; gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip); girq->parent_handler = ftgpio_gpio_irq_handler; girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), Loading @@ -458,23 +494,58 @@ the interrupt separately and go with it: .. code-block:: c /* Typical state container with dynamic irqchip */ /* Typical state container */ struct my_gpio { struct gpio_chip gc; struct irq_chip irq; }; static void my_gpio_mask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); /* * Perform any necessary action to mask the interrupt, * and then call into the core code to synchronise the * state. */ gpiochip_disable_irq(gc, d->hwirq); } static void my_gpio_unmask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); gpiochip_enable_irq(gc, d->hwirq); /* * Perform any necessary action to unmask the interrupt, * after having called into the core code to synchronise * the state. */ } /* * Statically populate the irqchip. Note that it is made const * (further indicated by the IRQCHIP_IMMUTABLE flag), and that * the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra * callbacks to the structure. */ static const struct irq_chip my_gpio_irq_chip = { .name = "my_gpio_irq", .irq_ack = my_gpio_ack_irq, .irq_mask = my_gpio_mask_irq, .irq_unmask = my_gpio_unmask_irq, .irq_set_type = my_gpio_set_irq_type, .flags = IRQCHIP_IMMUTABLE, /* Provide the gpio resource callbacks */ GPIOCHIP_IRQ_RESOURCE_HELPERS, }; 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; ret = devm_request_threaded_irq(dev, irq, NULL, irq_thread_fn, IRQF_ONESHOT, "my-chip", g); if (ret < 0) Loading @@ -482,7 +553,7 @@ the interrupt separately and go with it: /* Get a pointer to the gpio_irq_chip */ girq = &g->gc.irq; girq->chip = &g->irq; gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip); /* This will let us handle the parent IRQ in the driver */ girq->parent_handler = NULL; girq->num_parents = 0; Loading @@ -500,24 +571,61 @@ 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 */ static void my_gpio_mask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); /* * Perform any necessary action to mask the interrupt, * and then call into the core code to synchronise the * state. */ gpiochip_disable_irq(gc, d->hwirq); irq_mask_mask_parent(d); } static void my_gpio_unmask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); gpiochip_enable_irq(gc, d->hwirq); /* * Perform any necessary action to unmask the interrupt, * after having called into the core code to synchronise * the state. */ irq_mask_unmask_parent(d); } /* * Statically populate the irqchip. Note that it is made const * (further indicated by the IRQCHIP_IMMUTABLE flag), and that * the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra * callbacks to the structure. */ static const struct irq_chip my_gpio_irq_chip = { .name = "my_gpio_irq", .irq_ack = my_gpio_ack_irq, .irq_mask = my_gpio_mask_irq, .irq_unmask = my_gpio_unmask_irq, .irq_set_type = my_gpio_set_irq_type, .flags = IRQCHIP_IMMUTABLE, /* Provide the gpio resource callbacks */ GPIOCHIP_IRQ_RESOURCE_HELPERS, }; 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; gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip); girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; girq->fwnode = g->fwnode; Loading Loading @@ -605,8 +713,9 @@ When implementing an irqchip inside a GPIO driver, these two functions should typically be called in the .irq_disable() and .irq_enable() callbacks from the irqchip. When using the gpiolib irqchip helpers, these callbacks are automatically assigned. When IRQCHIP_IMMUTABLE is not advertised by the irqchip, these callbacks are automatically assigned. This behaviour is deprecated and on its way to be removed from the kernel. Real-Time compliance for GPIO IRQ chips Loading drivers/gpio/TODO +19 −0 Original line number Diff line number Diff line Loading @@ -178,3 +178,22 @@ discussed but the idea is to provide a low-level access point for debugging and hacking and to expose all lines without the need of any exporting. Also provide ample ammunition to shoot oneself in the foot, because this is debugfs after all. Moving over to immutable irq_chip structures Most of the gpio chips implementing interrupt support rely on gpiolib intercepting some of the irq_chip callbacks, preventing the structures from being made read-only and forcing duplication of structures that should otherwise be unique. The solution is to call into the gpiolib code when needed (resource management, enable/disable or unmask/mask callbacks), and to let the core code know about that by exposing a flag (IRQCHIP_IMMUTABLE) in the irq_chip structure. The irq_chip structure can then be made unique and const. A small number of drivers have been converted (pl061, tegra186, msm, amd, apple), and can be used as examples of how to proceed with this conversion. Note that drivers using the generic irqchip framework cannot be converted yet, but watch this space! drivers/gpio/gpio-pl061.c +23 −9 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ struct pl061 { void __iomem *base; struct gpio_chip gc; struct irq_chip irq_chip; int parent_irq; #ifdef CONFIG_PM Loading Loading @@ -241,6 +240,8 @@ static void pl061_irq_mask(struct irq_data *d) gpioie = readb(pl061->base + GPIOIE) & ~mask; writeb(gpioie, pl061->base + GPIOIE); raw_spin_unlock(&pl061->lock); gpiochip_disable_irq(gc, d->hwirq); } static void pl061_irq_unmask(struct irq_data *d) Loading @@ -250,6 +251,8 @@ static void pl061_irq_unmask(struct irq_data *d) u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); u8 gpioie; gpiochip_enable_irq(gc, d->hwirq); raw_spin_lock(&pl061->lock); gpioie = readb(pl061->base + GPIOIE) | mask; writeb(gpioie, pl061->base + GPIOIE); Loading Loading @@ -283,6 +286,24 @@ static int pl061_irq_set_wake(struct irq_data *d, unsigned int state) return irq_set_irq_wake(pl061->parent_irq, state); } static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); seq_printf(p, dev_name(gc->parent)); } static const struct irq_chip pl061_irq_chip = { .irq_ack = pl061_irq_ack, .irq_mask = pl061_irq_mask, .irq_unmask = pl061_irq_unmask, .irq_set_type = pl061_irq_type, .irq_set_wake = pl061_irq_set_wake, .irq_print_chip = pl061_irq_print_chip, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int pl061_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; Loading Loading @@ -315,13 +336,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) /* * irq_chip support */ pl061->irq_chip.name = dev_name(dev); pl061->irq_chip.irq_ack = pl061_irq_ack; pl061->irq_chip.irq_mask = pl061_irq_mask; pl061->irq_chip.irq_unmask = pl061_irq_unmask; pl061->irq_chip.irq_set_type = pl061_irq_type; pl061->irq_chip.irq_set_wake = pl061_irq_set_wake; writeb(0, pl061->base + GPIOIE); /* disable irqs */ irq = adev->irq[0]; if (!irq) Loading @@ -329,7 +343,7 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) pl061->parent_irq = irq; girq = &pl061->gc.irq; girq->chip = &pl061->irq_chip; gpio_irq_chip_set_chip(girq, &pl061_irq_chip); girq->parent_handler = pl061_irq_handler; girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), Loading drivers/gpio/gpio-tegra186.c +23 −9 Original line number Diff line number Diff line Loading @@ -80,7 +80,6 @@ struct tegra_gpio_soc { struct tegra_gpio { struct gpio_chip gpio; struct irq_chip intc; unsigned int num_irq; unsigned int *irq; Loading Loading @@ -372,6 +371,8 @@ static void tegra186_irq_mask(struct irq_data *data) value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); value &= ~TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT; writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); gpiochip_disable_irq(&gpio->gpio, data->hwirq); } static void tegra186_irq_unmask(struct irq_data *data) Loading @@ -385,6 +386,8 @@ static void tegra186_irq_unmask(struct irq_data *data) if (WARN_ON(base == NULL)) return; gpiochip_enable_irq(&gpio->gpio, data->hwirq); value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); value |= TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT; writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); Loading Loading @@ -456,6 +459,24 @@ static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } static void tegra186_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); seq_printf(p, dev_name(gc->parent)); } static const struct irq_chip tegra186_gpio_irq_chip = { .irq_ack = tegra186_irq_ack, .irq_mask = tegra186_irq_mask, .irq_unmask = tegra186_irq_unmask, .irq_set_type = tegra186_irq_set_type, .irq_set_wake = tegra186_irq_set_wake, .irq_print_chip = tegra186_irq_print_chip, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static void tegra186_gpio_irq(struct irq_desc *desc) { struct tegra_gpio *gpio = irq_desc_get_handler_data(desc); Loading Loading @@ -760,15 +781,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.of_xlate = tegra186_gpio_of_xlate; #endif /* CONFIG_OF_GPIO */ gpio->intc.name = dev_name(&pdev->dev); gpio->intc.irq_ack = tegra186_irq_ack; gpio->intc.irq_mask = tegra186_irq_mask; gpio->intc.irq_unmask = tegra186_irq_unmask; gpio->intc.irq_set_type = tegra186_irq_set_type; gpio->intc.irq_set_wake = tegra186_irq_set_wake; irq = &gpio->gpio.irq; irq->chip = &gpio->intc; gpio_irq_chip_set_chip(irq, &tegra186_gpio_irq_chip); irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec; Loading drivers/gpio/gpiolib.c +10 −3 Original line number Diff line number Diff line Loading @@ -1433,19 +1433,21 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) return irq_create_mapping(domain, offset); } static int gpiochip_irq_reqres(struct irq_data *d) int gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); return gpiochip_reqres_irq(gc, d->hwirq); } EXPORT_SYMBOL(gpiochip_irq_reqres); static void gpiochip_irq_relres(struct irq_data *d) void gpiochip_irq_relres(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); gpiochip_relres_irq(gc, d->hwirq); } EXPORT_SYMBOL(gpiochip_irq_relres); static void gpiochip_irq_mask(struct irq_data *d) { Loading Loading @@ -1485,6 +1487,11 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc) { struct irq_chip *irqchip = gc->irq.chip; if (irqchip->flags & IRQCHIP_IMMUTABLE) return; chip_warn(gc, "not an immutable chip, please consider fixing it!\n"); if (!irqchip->irq_request_resources && !irqchip->irq_release_resources) { irqchip->irq_request_resources = gpiochip_irq_reqres; Loading Loading @@ -1652,7 +1659,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc) irq_domain_remove(gc->irq.domain); } if (irqchip) { if (irqchip && !(irqchip->flags & IRQCHIP_IMMUTABLE)) { if (irqchip->irq_request_resources == gpiochip_irq_reqres) { irqchip->irq_request_resources = NULL; irqchip->irq_release_resources = NULL; Loading Loading
Documentation/driver-api/gpio/driver.rst +142 −33 Original line number Diff line number Diff line Loading @@ -417,30 +417,66 @@ 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 chained cascaded interrupt handler using the gpio_irq_chip: the gpio_irq_chip. Note how the mask/unmask (or disable/enable) functions call into the core gpiolib code: .. code-block:: c /* Typical state container with dynamic irqchip */ /* Typical state container */ struct my_gpio { struct gpio_chip gc; struct irq_chip irq; }; static void my_gpio_mask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); /* * Perform any necessary action to mask the interrupt, * and then call into the core code to synchronise the * state. */ gpiochip_disable_irq(gc, d->hwirq); } static void my_gpio_unmask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); gpiochip_enable_irq(gc, d->hwirq); /* * Perform any necessary action to unmask the interrupt, * after having called into the core code to synchronise * the state. */ } /* * Statically populate the irqchip. Note that it is made const * (further indicated by the IRQCHIP_IMMUTABLE flag), and that * the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra * callbacks to the structure. */ static const struct irq_chip my_gpio_irq_chip = { .name = "my_gpio_irq", .irq_ack = my_gpio_ack_irq, .irq_mask = my_gpio_mask_irq, .irq_unmask = my_gpio_unmask_irq, .irq_set_type = my_gpio_set_irq_type, .flags = IRQCHIP_IMMUTABLE, /* Provide the gpio resource callbacks */ GPIOCHIP_IRQ_RESOURCE_HELPERS, }; 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; gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip); girq->parent_handler = ftgpio_gpio_irq_handler; girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), Loading @@ -458,23 +494,58 @@ the interrupt separately and go with it: .. code-block:: c /* Typical state container with dynamic irqchip */ /* Typical state container */ struct my_gpio { struct gpio_chip gc; struct irq_chip irq; }; static void my_gpio_mask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); /* * Perform any necessary action to mask the interrupt, * and then call into the core code to synchronise the * state. */ gpiochip_disable_irq(gc, d->hwirq); } static void my_gpio_unmask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); gpiochip_enable_irq(gc, d->hwirq); /* * Perform any necessary action to unmask the interrupt, * after having called into the core code to synchronise * the state. */ } /* * Statically populate the irqchip. Note that it is made const * (further indicated by the IRQCHIP_IMMUTABLE flag), and that * the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra * callbacks to the structure. */ static const struct irq_chip my_gpio_irq_chip = { .name = "my_gpio_irq", .irq_ack = my_gpio_ack_irq, .irq_mask = my_gpio_mask_irq, .irq_unmask = my_gpio_unmask_irq, .irq_set_type = my_gpio_set_irq_type, .flags = IRQCHIP_IMMUTABLE, /* Provide the gpio resource callbacks */ GPIOCHIP_IRQ_RESOURCE_HELPERS, }; 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; ret = devm_request_threaded_irq(dev, irq, NULL, irq_thread_fn, IRQF_ONESHOT, "my-chip", g); if (ret < 0) Loading @@ -482,7 +553,7 @@ the interrupt separately and go with it: /* Get a pointer to the gpio_irq_chip */ girq = &g->gc.irq; girq->chip = &g->irq; gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip); /* This will let us handle the parent IRQ in the driver */ girq->parent_handler = NULL; girq->num_parents = 0; Loading @@ -500,24 +571,61 @@ 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 */ static void my_gpio_mask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); /* * Perform any necessary action to mask the interrupt, * and then call into the core code to synchronise the * state. */ gpiochip_disable_irq(gc, d->hwirq); irq_mask_mask_parent(d); } static void my_gpio_unmask_irq(struct irq_data *d) { struct gpio_chip *gc = irq_desc_get_handler_data(d); gpiochip_enable_irq(gc, d->hwirq); /* * Perform any necessary action to unmask the interrupt, * after having called into the core code to synchronise * the state. */ irq_mask_unmask_parent(d); } /* * Statically populate the irqchip. Note that it is made const * (further indicated by the IRQCHIP_IMMUTABLE flag), and that * the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra * callbacks to the structure. */ static const struct irq_chip my_gpio_irq_chip = { .name = "my_gpio_irq", .irq_ack = my_gpio_ack_irq, .irq_mask = my_gpio_mask_irq, .irq_unmask = my_gpio_unmask_irq, .irq_set_type = my_gpio_set_irq_type, .flags = IRQCHIP_IMMUTABLE, /* Provide the gpio resource callbacks */ GPIOCHIP_IRQ_RESOURCE_HELPERS, }; 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; gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip); girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; girq->fwnode = g->fwnode; Loading Loading @@ -605,8 +713,9 @@ When implementing an irqchip inside a GPIO driver, these two functions should typically be called in the .irq_disable() and .irq_enable() callbacks from the irqchip. When using the gpiolib irqchip helpers, these callbacks are automatically assigned. When IRQCHIP_IMMUTABLE is not advertised by the irqchip, these callbacks are automatically assigned. This behaviour is deprecated and on its way to be removed from the kernel. Real-Time compliance for GPIO IRQ chips Loading
drivers/gpio/TODO +19 −0 Original line number Diff line number Diff line Loading @@ -178,3 +178,22 @@ discussed but the idea is to provide a low-level access point for debugging and hacking and to expose all lines without the need of any exporting. Also provide ample ammunition to shoot oneself in the foot, because this is debugfs after all. Moving over to immutable irq_chip structures Most of the gpio chips implementing interrupt support rely on gpiolib intercepting some of the irq_chip callbacks, preventing the structures from being made read-only and forcing duplication of structures that should otherwise be unique. The solution is to call into the gpiolib code when needed (resource management, enable/disable or unmask/mask callbacks), and to let the core code know about that by exposing a flag (IRQCHIP_IMMUTABLE) in the irq_chip structure. The irq_chip structure can then be made unique and const. A small number of drivers have been converted (pl061, tegra186, msm, amd, apple), and can be used as examples of how to proceed with this conversion. Note that drivers using the generic irqchip framework cannot be converted yet, but watch this space!
drivers/gpio/gpio-pl061.c +23 −9 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ struct pl061 { void __iomem *base; struct gpio_chip gc; struct irq_chip irq_chip; int parent_irq; #ifdef CONFIG_PM Loading Loading @@ -241,6 +240,8 @@ static void pl061_irq_mask(struct irq_data *d) gpioie = readb(pl061->base + GPIOIE) & ~mask; writeb(gpioie, pl061->base + GPIOIE); raw_spin_unlock(&pl061->lock); gpiochip_disable_irq(gc, d->hwirq); } static void pl061_irq_unmask(struct irq_data *d) Loading @@ -250,6 +251,8 @@ static void pl061_irq_unmask(struct irq_data *d) u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); u8 gpioie; gpiochip_enable_irq(gc, d->hwirq); raw_spin_lock(&pl061->lock); gpioie = readb(pl061->base + GPIOIE) | mask; writeb(gpioie, pl061->base + GPIOIE); Loading Loading @@ -283,6 +286,24 @@ static int pl061_irq_set_wake(struct irq_data *d, unsigned int state) return irq_set_irq_wake(pl061->parent_irq, state); } static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); seq_printf(p, dev_name(gc->parent)); } static const struct irq_chip pl061_irq_chip = { .irq_ack = pl061_irq_ack, .irq_mask = pl061_irq_mask, .irq_unmask = pl061_irq_unmask, .irq_set_type = pl061_irq_type, .irq_set_wake = pl061_irq_set_wake, .irq_print_chip = pl061_irq_print_chip, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int pl061_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; Loading Loading @@ -315,13 +336,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) /* * irq_chip support */ pl061->irq_chip.name = dev_name(dev); pl061->irq_chip.irq_ack = pl061_irq_ack; pl061->irq_chip.irq_mask = pl061_irq_mask; pl061->irq_chip.irq_unmask = pl061_irq_unmask; pl061->irq_chip.irq_set_type = pl061_irq_type; pl061->irq_chip.irq_set_wake = pl061_irq_set_wake; writeb(0, pl061->base + GPIOIE); /* disable irqs */ irq = adev->irq[0]; if (!irq) Loading @@ -329,7 +343,7 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) pl061->parent_irq = irq; girq = &pl061->gc.irq; girq->chip = &pl061->irq_chip; gpio_irq_chip_set_chip(girq, &pl061_irq_chip); girq->parent_handler = pl061_irq_handler; girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), Loading
drivers/gpio/gpio-tegra186.c +23 −9 Original line number Diff line number Diff line Loading @@ -80,7 +80,6 @@ struct tegra_gpio_soc { struct tegra_gpio { struct gpio_chip gpio; struct irq_chip intc; unsigned int num_irq; unsigned int *irq; Loading Loading @@ -372,6 +371,8 @@ static void tegra186_irq_mask(struct irq_data *data) value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); value &= ~TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT; writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); gpiochip_disable_irq(&gpio->gpio, data->hwirq); } static void tegra186_irq_unmask(struct irq_data *data) Loading @@ -385,6 +386,8 @@ static void tegra186_irq_unmask(struct irq_data *data) if (WARN_ON(base == NULL)) return; gpiochip_enable_irq(&gpio->gpio, data->hwirq); value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); value |= TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT; writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); Loading Loading @@ -456,6 +459,24 @@ static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } static void tegra186_irq_print_chip(struct irq_data *data, struct seq_file *p) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); seq_printf(p, dev_name(gc->parent)); } static const struct irq_chip tegra186_gpio_irq_chip = { .irq_ack = tegra186_irq_ack, .irq_mask = tegra186_irq_mask, .irq_unmask = tegra186_irq_unmask, .irq_set_type = tegra186_irq_set_type, .irq_set_wake = tegra186_irq_set_wake, .irq_print_chip = tegra186_irq_print_chip, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static void tegra186_gpio_irq(struct irq_desc *desc) { struct tegra_gpio *gpio = irq_desc_get_handler_data(desc); Loading Loading @@ -760,15 +781,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.of_xlate = tegra186_gpio_of_xlate; #endif /* CONFIG_OF_GPIO */ gpio->intc.name = dev_name(&pdev->dev); gpio->intc.irq_ack = tegra186_irq_ack; gpio->intc.irq_mask = tegra186_irq_mask; gpio->intc.irq_unmask = tegra186_irq_unmask; gpio->intc.irq_set_type = tegra186_irq_set_type; gpio->intc.irq_set_wake = tegra186_irq_set_wake; irq = &gpio->gpio.irq; irq->chip = &gpio->intc; gpio_irq_chip_set_chip(irq, &tegra186_gpio_irq_chip); irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec; Loading
drivers/gpio/gpiolib.c +10 −3 Original line number Diff line number Diff line Loading @@ -1433,19 +1433,21 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) return irq_create_mapping(domain, offset); } static int gpiochip_irq_reqres(struct irq_data *d) int gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); return gpiochip_reqres_irq(gc, d->hwirq); } EXPORT_SYMBOL(gpiochip_irq_reqres); static void gpiochip_irq_relres(struct irq_data *d) void gpiochip_irq_relres(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); gpiochip_relres_irq(gc, d->hwirq); } EXPORT_SYMBOL(gpiochip_irq_relres); static void gpiochip_irq_mask(struct irq_data *d) { Loading Loading @@ -1485,6 +1487,11 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc) { struct irq_chip *irqchip = gc->irq.chip; if (irqchip->flags & IRQCHIP_IMMUTABLE) return; chip_warn(gc, "not an immutable chip, please consider fixing it!\n"); if (!irqchip->irq_request_resources && !irqchip->irq_release_resources) { irqchip->irq_request_resources = gpiochip_irq_reqres; Loading Loading @@ -1652,7 +1659,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc) irq_domain_remove(gc->irq.domain); } if (irqchip) { if (irqchip && !(irqchip->flags & IRQCHIP_IMMUTABLE)) { if (irqchip->irq_request_resources == gpiochip_irq_reqres) { irqchip->irq_request_resources = NULL; irqchip->irq_release_resources = NULL; Loading