On Thu, Jul 30, 2020 at 06:28:02PM +0300, Serge Semin wrote: > GPIO-lib provides a ready-to-use interface to initialize an IRQ-chip on > top of a GPIO chip. It's better from maintainability and readability > point of view to use one instead of supporting a hand-written Generic > IRQ-chip-based implementation. Moreover the new implementation won't > cause much functional overhead but will provide a cleaner driver code. > All of that makes the DW APB GPIO driver conversion pretty much justified > especially seeing a tendency of the other GPIO drivers getting converted > too. > > Here is what we do in the framework of this commit to convert the driver > to using the GPIO-lib-based IRQ-chip interface: > > 1) IRQ ack, mask and unmask callbacks are locally defined instead of > using the Generic IRQ-chip ones. > > 2) An irq_chip structure instance is embedded into the dwapb_gpio > private data. Note we can't have a static instance of that structure since > GPIO-lib will add some hooks into it by calling gpiochip_set_irq_hooks(). > A warning about that would have been printed by the GPIO-lib code if we > used a single irq_chip structure instance for multiple DW APB GPIO > controllers. > > 3) Initialize the gpio_irq_chip structure embedded into the gpio_chip > descriptor. By default there is no IRQ enabled so any event raised will be > handled by the handle_bad_irq() IRQ flow handler. If DW APB GPIO IP-core > is synthesized to have non-shared reference IRQ-lines, then as before the > hierarchical and cascaded cases are distinguished by checking how many > parental IRQs are defined. (Note irq_set_chained_handler_and_data() won't > initialize IRQs, which descriptors couldn't be found.) If DW APB GPIO IP > is used on a platform with shared IRQ line, then we simply won't let the > GPIO-lib to initialize the parental IRQs, but will handle them locally in > the driver. > > 4) Discard linear IRQ-domain and Generic IRQ-chip initialization, since > GPIO-lib IRQ-chip interface will create a new domain and accept a standard > IRQ-chip structure pointer based on the setting we provided in the > gpio_irq_chip structure. > > 5) Manually select a proper IRQ flow handler directly in the > irq_set_type() callback by calling irq_set_handler_locked() method, since > an ordinary (not Generic) irq_chip descriptor is now utilized. Note this > shalln't give any regression > > 6) Alter CONFIG_GPIO_DWAPB kernel config to select > CONFIG_GPIOLIB_IRQCHIP instead of CONFIG_GENERIC_IRQ_CHIP. > > Note neither 4) nor 5) shall cause a regression of commit 6a2f4b7dadd5 > ("gpio: dwapb: use a second irq chip"), since the later isn't properly > used here anyway. Looks good, thanks! Reviewed-by: Andy Shevchenko <andy.shevchenko@xxxxxxxxx> > Signed-off-by: Serge Semin <Sergey.Semin@xxxxxxxxxxxxxxxxxxxx> > > --- > > Note in this patch we omit the next GPIO-lib IRQ-chip settings > initialization: > gc->irq.map > gc->irq.init_valid_mask > > That's intentionally done in order to keep the driver functionality at the > same level as before the conversion: each GPIO line will be registered as > the source of IRQs, no matter whether DW APB GPIO IP is configured to > generate combined or multiple IRQ signals. > > If more thorough mapping is needed, the driver should be altered to detect > the IRQ signaling mode (for instance by checking a dedicated DT-property). > Then based on that it will be able to create a proper hardware GPIO-IRQ > to/from parental IRQs mapping and valid hardware GPIO-IRQs mask. > > Changelog v2: > - Replace gc->to_irq() with irq_find_mapping() method. > - Refactor dwapb_irq_set_type() method to directly set IRQ flow handler > instead of using a temporary variable. > - Initialize GPIOlib IRQ-chip settings before calling request_irq() > method. > - Add a notice regarding regression of commit 6a2f4b7dadd5 ("gpio: > dwapb: use a second irq chip"). > - Move the acpi_gpiochip_{request,free}_interrupts() methods invocation > removal to a dedicated commit. > - Move GPIO-chip to_irq callback removal to a dedicated commit. > - Introduce dwapb_convert_irqs() method to convert the sparse parental > IRQs array into an array of linearly distributed IRQs correctly > perceived by GPIO-lib. > > Changelog v3: > - Add blank lines to the commit message to make it more readable. > - Dynamically allocate memory for the IRQ-chip-related data. > --- > drivers/gpio/Kconfig | 2 +- > drivers/gpio/gpio-dwapb.c | 203 +++++++++++++++++++++----------------- > 2 files changed, 111 insertions(+), 94 deletions(-) > > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig > index c6b5c65c8405..bcd3e541a52b 100644 > --- a/drivers/gpio/Kconfig > +++ b/drivers/gpio/Kconfig > @@ -202,7 +202,7 @@ config GPIO_DAVINCI > config GPIO_DWAPB > tristate "Synopsys DesignWare APB GPIO driver" > select GPIO_GENERIC > - select GENERIC_IRQ_CHIP > + select GPIOLIB_IRQCHIP > help > Say Y or M here to build support for the Synopsys DesignWare APB > GPIO block. > diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c > index f34001152850..eb3a6da453ff 100644 > --- a/drivers/gpio/gpio-dwapb.c > +++ b/drivers/gpio/gpio-dwapb.c > @@ -13,7 +13,6 @@ > #include <linux/io.h> > #include <linux/ioport.h> > #include <linux/irq.h> > -#include <linux/irqdomain.h> > #include <linux/module.h> > #include <linux/of.h> > #include <linux/of_address.h> > @@ -83,8 +82,15 @@ struct dwapb_context { > }; > #endif > > +struct dwapb_gpio_port_irqchip { > + struct irq_chip irqchip; > + unsigned int nr_irqs; > + unsigned int irq[DWAPB_MAX_GPIOS]; > +}; > + > struct dwapb_gpio_port { > struct gpio_chip gc; > + struct dwapb_gpio_port_irqchip *pirq; > bool is_registered; > struct dwapb_gpio *gpio; > #ifdef CONFIG_PM_SLEEP > @@ -92,13 +98,14 @@ struct dwapb_gpio_port { > #endif > unsigned int idx; > }; > +#define to_dwapb_gpio(_gc) \ > + (container_of(_gc, struct dwapb_gpio_port, gc)->gpio) > > struct dwapb_gpio { > struct device *dev; > void __iomem *regs; > struct dwapb_gpio_port *ports; > unsigned int nr_ports; > - struct irq_domain *domain; > unsigned int flags; > struct reset_control *rst; > struct clk_bulk_data clks[DWAPB_NR_CLOCKS]; > @@ -193,12 +200,13 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs) > > static u32 dwapb_do_irq(struct dwapb_gpio *gpio) > { > + struct gpio_chip *gc = &gpio->ports[0].gc; > unsigned long irq_status; > irq_hw_number_t hwirq; > > irq_status = dwapb_read(gpio, GPIO_INTSTATUS); > for_each_set_bit(hwirq, &irq_status, DWAPB_MAX_GPIOS) { > - int gpio_irq = irq_find_mapping(gpio->domain, hwirq); > + int gpio_irq = irq_find_mapping(gc->irq.domain, hwirq); > u32 irq_type = irq_get_trigger_type(gpio_irq); > > generic_handle_irq(gpio_irq); > @@ -225,11 +233,48 @@ static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id) > return IRQ_RETVAL(dwapb_do_irq(dev_id)); > } > > +static void dwapb_irq_ack(struct irq_data *d) > +{ > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); > + u32 val = BIT(irqd_to_hwirq(d)); > + unsigned long flags; > + > + spin_lock_irqsave(&gc->bgpio_lock, flags); > + dwapb_write(gpio, GPIO_PORTA_EOI, val); > + spin_unlock_irqrestore(&gc->bgpio_lock, flags); > +} > + > +static void dwapb_irq_mask(struct irq_data *d) > +{ > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); > + unsigned long flags; > + u32 val; > + > + spin_lock_irqsave(&gc->bgpio_lock, flags); > + val = dwapb_read(gpio, GPIO_INTMASK) | BIT(irqd_to_hwirq(d)); > + dwapb_write(gpio, GPIO_INTMASK, val); > + spin_unlock_irqrestore(&gc->bgpio_lock, flags); > +} > + > +static void dwapb_irq_unmask(struct irq_data *d) > +{ > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); > + unsigned long flags; > + u32 val; > + > + spin_lock_irqsave(&gc->bgpio_lock, flags); > + val = dwapb_read(gpio, GPIO_INTMASK) & ~BIT(irqd_to_hwirq(d)); > + dwapb_write(gpio, GPIO_INTMASK, val); > + spin_unlock_irqrestore(&gc->bgpio_lock, flags); > +} > + > static void dwapb_irq_enable(struct irq_data *d) > { > - struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); > - struct dwapb_gpio *gpio = igc->private; > - struct gpio_chip *gc = &gpio->ports[0].gc; > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); > unsigned long flags; > u32 val; > > @@ -242,9 +287,8 @@ static void dwapb_irq_enable(struct irq_data *d) > > static void dwapb_irq_disable(struct irq_data *d) > { > - struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); > - struct dwapb_gpio *gpio = igc->private; > - struct gpio_chip *gc = &gpio->ports[0].gc; > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); > unsigned long flags; > u32 val; > > @@ -257,9 +301,8 @@ static void dwapb_irq_disable(struct irq_data *d) > > static int dwapb_irq_set_type(struct irq_data *d, u32 type) > { > - struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); > - struct dwapb_gpio *gpio = igc->private; > - struct gpio_chip *gc = &gpio->ports[0].gc; > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); > irq_hw_number_t bit = irqd_to_hwirq(d); > unsigned long level, polarity, flags; > > @@ -293,7 +336,10 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) > break; > } > > - irq_setup_alt_chip(d, type); > + if (type & IRQ_TYPE_LEVEL_MASK) > + irq_set_handler_locked(d, handle_level_irq); > + else if (type & IRQ_TYPE_EDGE_BOTH) > + irq_set_handler_locked(d, handle_edge_irq); > > dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level); > if (type != IRQ_TYPE_EDGE_BOTH) > @@ -354,79 +400,67 @@ static int dwapb_gpio_set_config(struct gpio_chip *gc, unsigned offset, > return dwapb_gpio_set_debounce(gc, offset, debounce); > } > > +static int dwapb_convert_irqs(struct dwapb_gpio_port_irqchip *pirq, > + struct dwapb_port_property *pp) > +{ > + int i; > + > + /* Group all available IRQs into an array of parental IRQs. */ > + for (i = 0; i < pp->ngpio; ++i) { > + if (!pp->irq[i]) > + continue; > + > + pirq->irq[pirq->nr_irqs++] = pp->irq[i]; > + } > + > + return pirq->nr_irqs ? 0 : -ENOENT; > +} > + > static void dwapb_configure_irqs(struct dwapb_gpio *gpio, > struct dwapb_gpio_port *port, > struct dwapb_port_property *pp) > { > + struct dwapb_gpio_port_irqchip *pirq; > struct gpio_chip *gc = &port->gc; > - struct fwnode_handle *fwnode = pp->fwnode; > - struct irq_chip_generic *irq_gc = NULL; > - unsigned int ngpio = gc->ngpio; > - struct irq_chip_type *ct; > - irq_hw_number_t hwirq; > - int err, i; > - > - if (memchr_inv(pp->irq, 0, sizeof(pp->irq)) == NULL) { > - dev_warn(gpio->dev, "no IRQ for port%d\n", pp->idx); > - return; > - } > - > - gpio->domain = irq_domain_create_linear(fwnode, ngpio, > - &irq_generic_chip_ops, gpio); > - if (!gpio->domain) > - return; > + struct gpio_irq_chip *girq; > + int err; > > - err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 2, > - DWAPB_DRIVER_NAME, handle_bad_irq, > - IRQ_NOREQUEST, 0, > - IRQ_GC_INIT_NESTED_LOCK); > - if (err) { > - dev_info(gpio->dev, "irq_alloc_domain_generic_chips failed\n"); > - irq_domain_remove(gpio->domain); > - gpio->domain = NULL; > + pirq = devm_kzalloc(gpio->dev, sizeof(*pirq), GFP_KERNEL); > + if (!pirq) > return; > - } > > - irq_gc = irq_get_domain_generic_chip(gpio->domain, 0); > - if (!irq_gc) { > - irq_domain_remove(gpio->domain); > - gpio->domain = NULL; > - return; > + if (dwapb_convert_irqs(pirq, pp)) { > + dev_warn(gpio->dev, "no IRQ for port%d\n", pp->idx); > + goto err_kfree_pirq; > } > > - irq_gc->reg_base = gpio->regs; > - irq_gc->private = gpio; > - > - for (i = 0; i < 2; i++) { > - ct = &irq_gc->chip_types[i]; > - ct->chip.irq_ack = irq_gc_ack_set_bit; > - ct->chip.irq_mask = irq_gc_mask_set_bit; > - ct->chip.irq_unmask = irq_gc_mask_clr_bit; > - ct->chip.irq_set_type = dwapb_irq_set_type; > - ct->chip.irq_enable = dwapb_irq_enable; > - ct->chip.irq_disable = dwapb_irq_disable; > + girq = &gc->irq; > + girq->handler = handle_bad_irq; > + girq->default_type = IRQ_TYPE_NONE; > + > + port->pirq = pirq; > + pirq->irqchip.name = DWAPB_DRIVER_NAME; > + pirq->irqchip.irq_ack = dwapb_irq_ack; > + pirq->irqchip.irq_mask = dwapb_irq_mask; > + pirq->irqchip.irq_unmask = dwapb_irq_unmask; > + pirq->irqchip.irq_set_type = dwapb_irq_set_type; > + pirq->irqchip.irq_enable = dwapb_irq_enable; > + pirq->irqchip.irq_disable = dwapb_irq_disable; > #ifdef CONFIG_PM_SLEEP > - ct->chip.irq_set_wake = dwapb_irq_set_wake; > + pirq->irqchip.irq_set_wake = dwapb_irq_set_wake; > #endif > - ct->regs.ack = gpio_reg_convert(gpio, GPIO_PORTA_EOI); > - ct->regs.mask = gpio_reg_convert(gpio, GPIO_INTMASK); > - ct->type = IRQ_TYPE_LEVEL_MASK; > - } > - > - irq_gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; > - irq_gc->chip_types[0].handler = handle_level_irq; > - irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; > - irq_gc->chip_types[1].handler = handle_edge_irq; > > if (!pp->irq_shared) { > - int i; > - > - for (i = 0; i < pp->ngpio; i++) { > - if (pp->irq[i]) > - irq_set_chained_handler_and_data(pp->irq[i], > - dwapb_irq_handler, gpio); > - } > + girq->num_parents = pirq->nr_irqs; > + girq->parents = pirq->irq; > + girq->parent_handler_data = gpio; > + girq->parent_handler = dwapb_irq_handler; > } else { > + /* This will let us handle the parent IRQ in the driver */ > + girq->num_parents = 0; > + girq->parents = NULL; > + girq->parent_handler = NULL; > + > /* > * Request a shared IRQ since where MFD would have devices > * using the same irq pin > @@ -436,33 +470,18 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio, > IRQF_SHARED, DWAPB_DRIVER_NAME, gpio); > if (err) { > dev_err(gpio->dev, "error requesting IRQ\n"); > - irq_domain_remove(gpio->domain); > - gpio->domain = NULL; > - return; > + goto err_kfree_pirq; > } > } > > - for (hwirq = 0; hwirq < ngpio; hwirq++) > - irq_create_mapping(gpio->domain, hwirq); > + girq->chip = &pirq->irqchip; > > port->gc.to_irq = dwapb_gpio_to_irq; > -} > - > -static void dwapb_irq_teardown(struct dwapb_gpio *gpio) > -{ > - struct dwapb_gpio_port *port = &gpio->ports[0]; > - struct gpio_chip *gc = &port->gc; > - unsigned int ngpio = gc->ngpio; > - irq_hw_number_t hwirq; > - > - if (!gpio->domain) > - return; > > - for (hwirq = 0; hwirq < ngpio; hwirq++) > - irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq)); > + return; > > - irq_domain_remove(gpio->domain); > - gpio->domain = NULL; > +err_kfree_pirq: > + devm_kfree(gpio->dev, pirq); > } > > static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, > @@ -699,7 +718,6 @@ static int dwapb_gpio_probe(struct platform_device *pdev) > > out_unregister: > dwapb_gpio_unregister(gpio); > - dwapb_irq_teardown(gpio); > clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks); > > return err; > @@ -710,7 +728,6 @@ static int dwapb_gpio_remove(struct platform_device *pdev) > struct dwapb_gpio *gpio = platform_get_drvdata(pdev); > > dwapb_gpio_unregister(gpio); > - dwapb_irq_teardown(gpio); > reset_control_assert(gpio->rst); > clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks); > > -- > 2.27.0 > -- With Best Regards, Andy Shevchenko