Re: [PATCH v3] gpio: pl061: Support implementations without GPIOINTR line

From: Andy Shevchenko
Date: Fri Mar 19 2021 - 10:35:24 EST


On Fri, Mar 19, 2021 at 3:12 PM Alexander A Sverdlin
<alexander.sverdlin@xxxxxxxxx> wrote:
>
> From: Alexander Sverdlin <alexander.sverdlin@xxxxxxxxx>
>
> There are several implementations of PL061 which lack GPIOINTR signal in
> hardware and only have individual GPIOMIS[7:0] interrupts. Use the
> hierarchical interrupt support of the gpiolib in these cases (if at least 8
> IRQs are configured for the PL061).
>
> One in-tree example is arch/arm/boot/dts/axm55xx.dtsi, PL061 instances have
> 8 IRQs defined, but current driver supports only the first one, so only one
> pin would work as IRQ trigger.

an IRQ

I'm wondering if the GPIO library support for IRQ hierarchy is what
you are looking for.

> Link: https://lore.kernel.org/linux-gpio/CACRpkdZpYzpMDWqJobSYH=JHgB74HbCQihOtexs+sVyo6SRJdA@xxxxxxxxxxxxxx/
> Signed-off-by: Alexander Sverdlin <alexander.sverdlin@xxxxxxxxx>
> ---
> Changelog:
> v3: pl061_populate_parent_fwspec() -> pl061_populate_parent_alloc_arg()
> v2: Add pl061_populate_parent_fwspec()
>
> drivers/gpio/Kconfig | 1 +
> drivers/gpio/gpio-pl061.c | 97 +++++++++++++++++++++++++++++++++++++++++++----
> 2 files changed, 91 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index e3607ec..456c0a5 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -469,6 +469,7 @@ config GPIO_PL061
> depends on ARM_AMBA
> select IRQ_DOMAIN
> select GPIOLIB_IRQCHIP
> + select IRQ_DOMAIN_HIERARCHY

A nit-pick: perhaps group IRQ_ together, like

select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
select GPIOLIB_IRQCHIP

?

> help
> Say yes here to support the PrimeCell PL061 GPIO device
>
> diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
> index f1b53dd..5bfb5f6 100644
> --- a/drivers/gpio/gpio-pl061.c
> +++ b/drivers/gpio/gpio-pl061.c
> @@ -24,6 +24,7 @@
> #include <linux/slab.h>
> #include <linux/pinctrl/consumer.h>
> #include <linux/pm.h>

> +#include <linux/of_irq.h>

A nit-pick: Perhaps before linux/p* ?

> #define GPIODIR 0x400
> #define GPIOIS 0x404
> @@ -283,6 +284,69 @@ static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
> return irq_set_irq_wake(pl061->parent_irq, state);
> }
>
> +static int pl061_child_to_parent_hwirq(struct gpio_chip *gc, unsigned int child,
> + unsigned int child_type,
> + unsigned int *parent,
> + unsigned int *parent_type)
> +{
> + struct amba_device *adev = to_amba_device(gc->parent);
> + unsigned int irq = adev->irq[child];
> + struct irq_data *d = irq_get_irq_data(irq);
> +
> + if (!d)
> + return -EINVAL;
> +
> + *parent_type = irqd_get_trigger_type(d);
> + *parent = irqd_to_hwirq(d);
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static void *pl061_populate_parent_alloc_arg(struct gpio_chip *gc,
> + unsigned int parent_hwirq,
> + unsigned int parent_type)
> +{
> + struct device_node *dn = to_of_node(gc->irq.fwnode);
> + struct of_phandle_args pha;
> + struct irq_fwspec *fwspec;
> + int i;
> +
> + if (WARN_ON(!dn))
> + return NULL;
> +
> + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
> + if (!fwspec)
> + return NULL;
> +
> + /*
> + * This brute-force here is because of the fact PL061 is often paired
> + * with GIC-v3, which has 3-cell IRQ specifier (SPI/PPI selection), and
> + * unexpected range shifts in hwirq mapping (SPI IRQs are shifted by
> + * 32). So this is about reversing of gic_irq_domain_translate().
> + */
> + for (i = 0; i < PL061_GPIO_NR; i++) {
> + unsigned int p, pt;
> +
> + if (pl061_child_to_parent_hwirq(gc, i, parent_type, &p, &pt))
> + continue;
> + if (p == parent_hwirq)
> + break;
> + }
> + if (WARN_ON(i == PL061_GPIO_NR))
> + return NULL;
> +
> + if (WARN_ON(of_irq_parse_one(dn, i, &pha)))
> + return NULL;
> +
> + fwspec->fwnode = gc->irq.parent_domain->fwnode;
> + fwspec->param_count = pha.args_count;
> + for (i = 0; i < pha.args_count; i++)
> + fwspec->param[i] = pha.args[i];
> +
> + return fwspec;
> +}
> +#endif
> +
> static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
> {
> struct device *dev = &adev->dev;
> @@ -330,16 +394,35 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
>
> girq = &pl061->gc.irq;
> girq->chip = &pl061->irq_chip;
> - girq->parent_handler = pl061_irq_handler;
> - girq->num_parents = 1;
> - girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
> - GFP_KERNEL);
> - if (!girq->parents)
> - return -ENOMEM;
> - girq->parents[0] = irq;
> girq->default_type = IRQ_TYPE_NONE;
> girq->handler = handle_bad_irq;
>
> + /*
> + * There are some PL061 implementations which lack GPIOINTR in hardware
> + * and only have individual GPIOMIS[7:0] signals. We distinguish them by
> + * the number of IRQs assigned to the AMBA device.
> + */
> + if (adev->irq[PL061_GPIO_NR - 1]) {
> + girq->fwnode = dev->fwnode;
> + girq->parent_domain =
> + irq_get_irq_data(adev->irq[PL061_GPIO_NR - 1])->domain;
> + girq->child_to_parent_hwirq = pl061_child_to_parent_hwirq;
> +#ifdef CONFIG_OF
> + girq->populate_parent_alloc_arg =
> + pl061_populate_parent_alloc_arg;
> +#endif
> + } else {
> + WARN_ON(adev->irq[1]);
> +
> + girq->parent_handler = pl061_irq_handler;
> + girq->num_parents = 1;
> + girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
> + GFP_KERNEL);
> + if (!girq->parents)
> + return -ENOMEM;
> + girq->parents[0] = irq;
> + }
> +
> ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
> if (ret)
> return ret;
> --
> 2.10.2
>


--
With Best Regards,
Andy Shevchenko