Re: [PATCH v3] gpio: pl061: Support implementations without GPIOINTR line
From: Andy Shevchenko <hidden>
Date: 2021-03-19 14:35:19
Also in:
lkml
On Fri, Mar 19, 2021 at 3:12 PM Alexander A Sverdlin [off-list ref] wrote:
From: Alexander Sverdlin <redacted> 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.
quoted hunk ↗ jump to hunk
Link: https://lore.kernel.org/linux-gpio/CACRpkdZpYzpMDWqJobSYH=JHgB74HbCQihOtexs+sVyo6SRJdA@mail.gmail.com/ (local) Signed-off-by: Alexander Sverdlin <redacted> --- 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
?
quoted hunk ↗ jump to hunk
help Say yes here to support the PrimeCell PL061 GPIO devicediff --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* ?
quoted hunk ↗ jump to hunk
#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