gpio: dwapb: Convert driver to using the GPIO-lib-based IRQ-chip

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 6a2f4b7dad
("gpio: dwapb: use a second irq chip"), since the later isn't properly
used here anyway.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20200730152808.2955-6-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Serge Semin 2020-07-30 18:28:02 +03:00 коммит произвёл Linus Walleij
Родитель f9f890ba2b
Коммит 0ea683931a
2 изменённых файлов: 111 добавлений и 94 удалений

Просмотреть файл

@ -202,7 +202,7 @@ config GPIO_DAVINCI
config GPIO_DWAPB config GPIO_DWAPB
tristate "Synopsys DesignWare APB GPIO driver" tristate "Synopsys DesignWare APB GPIO driver"
select GPIO_GENERIC select GPIO_GENERIC
select GENERIC_IRQ_CHIP select GPIOLIB_IRQCHIP
help help
Say Y or M here to build support for the Synopsys DesignWare APB Say Y or M here to build support for the Synopsys DesignWare APB
GPIO block. GPIO block.

Просмотреть файл

@ -13,7 +13,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
@ -83,8 +82,15 @@ struct dwapb_context {
}; };
#endif #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 dwapb_gpio_port {
struct gpio_chip gc; struct gpio_chip gc;
struct dwapb_gpio_port_irqchip *pirq;
bool is_registered; bool is_registered;
struct dwapb_gpio *gpio; struct dwapb_gpio *gpio;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
@ -92,13 +98,14 @@ struct dwapb_gpio_port {
#endif #endif
unsigned int idx; unsigned int idx;
}; };
#define to_dwapb_gpio(_gc) \
(container_of(_gc, struct dwapb_gpio_port, gc)->gpio)
struct dwapb_gpio { struct dwapb_gpio {
struct device *dev; struct device *dev;
void __iomem *regs; void __iomem *regs;
struct dwapb_gpio_port *ports; struct dwapb_gpio_port *ports;
unsigned int nr_ports; unsigned int nr_ports;
struct irq_domain *domain;
unsigned int flags; unsigned int flags;
struct reset_control *rst; struct reset_control *rst;
struct clk_bulk_data clks[DWAPB_NR_CLOCKS]; 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) static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
{ {
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long irq_status; unsigned long irq_status;
irq_hw_number_t hwirq; irq_hw_number_t hwirq;
irq_status = dwapb_read(gpio, GPIO_INTSTATUS); irq_status = dwapb_read(gpio, GPIO_INTSTATUS);
for_each_set_bit(hwirq, &irq_status, DWAPB_MAX_GPIOS) { 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); u32 irq_type = irq_get_trigger_type(gpio_irq);
generic_handle_irq(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)); 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) static void dwapb_irq_enable(struct irq_data *d)
{ {
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct dwapb_gpio *gpio = igc->private; struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags; unsigned long flags;
u32 val; u32 val;
@ -242,9 +287,8 @@ static void dwapb_irq_enable(struct irq_data *d)
static void dwapb_irq_disable(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 gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct dwapb_gpio *gpio = igc->private; struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags; unsigned long flags;
u32 val; 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) 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 gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct dwapb_gpio *gpio = igc->private; struct dwapb_gpio *gpio = to_dwapb_gpio(gc);
struct gpio_chip *gc = &gpio->ports[0].gc;
irq_hw_number_t bit = irqd_to_hwirq(d); irq_hw_number_t bit = irqd_to_hwirq(d);
unsigned long level, polarity, flags; unsigned long level, polarity, flags;
@ -293,7 +336,10 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
break; 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); dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
if (type != IRQ_TYPE_EDGE_BOTH) 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); 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, static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
struct dwapb_gpio_port *port, struct dwapb_gpio_port *port,
struct dwapb_port_property *pp) struct dwapb_port_property *pp)
{ {
struct dwapb_gpio_port_irqchip *pirq;
struct gpio_chip *gc = &port->gc; struct gpio_chip *gc = &port->gc;
struct fwnode_handle *fwnode = pp->fwnode; struct gpio_irq_chip *girq;
struct irq_chip_generic *irq_gc = NULL; int err;
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) { pirq = devm_kzalloc(gpio->dev, sizeof(*pirq), GFP_KERNEL);
if (!pirq)
return;
if (dwapb_convert_irqs(pirq, pp)) {
dev_warn(gpio->dev, "no IRQ for port%d\n", pp->idx); dev_warn(gpio->dev, "no IRQ for port%d\n", pp->idx);
return; goto err_kfree_pirq;
} }
gpio->domain = irq_domain_create_linear(fwnode, ngpio, girq = &gc->irq;
&irq_generic_chip_ops, gpio); girq->handler = handle_bad_irq;
if (!gpio->domain) girq->default_type = IRQ_TYPE_NONE;
return;
err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 2, port->pirq = pirq;
DWAPB_DRIVER_NAME, handle_bad_irq, pirq->irqchip.name = DWAPB_DRIVER_NAME;
IRQ_NOREQUEST, 0, pirq->irqchip.irq_ack = dwapb_irq_ack;
IRQ_GC_INIT_NESTED_LOCK); pirq->irqchip.irq_mask = dwapb_irq_mask;
if (err) { pirq->irqchip.irq_unmask = dwapb_irq_unmask;
dev_info(gpio->dev, "irq_alloc_domain_generic_chips failed\n"); pirq->irqchip.irq_set_type = dwapb_irq_set_type;
irq_domain_remove(gpio->domain); pirq->irqchip.irq_enable = dwapb_irq_enable;
gpio->domain = NULL; pirq->irqchip.irq_disable = dwapb_irq_disable;
return;
}
irq_gc = irq_get_domain_generic_chip(gpio->domain, 0);
if (!irq_gc) {
irq_domain_remove(gpio->domain);
gpio->domain = NULL;
return;
}
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;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
ct->chip.irq_set_wake = dwapb_irq_set_wake; pirq->irqchip.irq_set_wake = dwapb_irq_set_wake;
#endif #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) { if (!pp->irq_shared) {
int i; girq->num_parents = pirq->nr_irqs;
girq->parents = pirq->irq;
for (i = 0; i < pp->ngpio; i++) { girq->parent_handler_data = gpio;
if (pp->irq[i]) girq->parent_handler = dwapb_irq_handler;
irq_set_chained_handler_and_data(pp->irq[i],
dwapb_irq_handler, gpio);
}
} else { } 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 * Request a shared IRQ since where MFD would have devices
* using the same irq pin * using the same irq pin
@ -436,33 +470,18 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
IRQF_SHARED, DWAPB_DRIVER_NAME, gpio); IRQF_SHARED, DWAPB_DRIVER_NAME, gpio);
if (err) { if (err) {
dev_err(gpio->dev, "error requesting IRQ\n"); dev_err(gpio->dev, "error requesting IRQ\n");
irq_domain_remove(gpio->domain); goto err_kfree_pirq;
gpio->domain = NULL;
return;
} }
} }
for (hwirq = 0; hwirq < ngpio; hwirq++) girq->chip = &pirq->irqchip;
irq_create_mapping(gpio->domain, hwirq);
port->gc.to_irq = dwapb_gpio_to_irq; port->gc.to_irq = dwapb_gpio_to_irq;
}
static void dwapb_irq_teardown(struct dwapb_gpio *gpio) return;
{
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) err_kfree_pirq:
return; devm_kfree(gpio->dev, pirq);
for (hwirq = 0; hwirq < ngpio; hwirq++)
irq_dispose_mapping(irq_find_mapping(gpio->domain, hwirq));
irq_domain_remove(gpio->domain);
gpio->domain = NULL;
} }
static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, 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: out_unregister:
dwapb_gpio_unregister(gpio); dwapb_gpio_unregister(gpio);
dwapb_irq_teardown(gpio);
clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks); clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);
return err; return err;
@ -710,7 +728,6 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
struct dwapb_gpio *gpio = platform_get_drvdata(pdev); struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
dwapb_gpio_unregister(gpio); dwapb_gpio_unregister(gpio);
dwapb_irq_teardown(gpio);
reset_control_assert(gpio->rst); reset_control_assert(gpio->rst);
clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks); clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks);