Thread (6 messages) 6 messages, 3 authors, 2021-03-22

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 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* ?
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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help