2021-03-18 19:14:33

by Alexander Sverdlin

[permalink] [raw]
Subject: [PATCH v2] gpio: pl061: Support implementations without GPIOINTR line

From: Alexander Sverdlin <[email protected]>

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.

Link: https://lore.kernel.org/linux-gpio/CACRpkdZpYzpMDWqJobSYH=JHgB74HbCQihOtexs+sVyo6SRJdA@mail.gmail.com/
Signed-off-by: Alexander Sverdlin <[email protected]>
---
Changelog:
v2: Add pl061_populate_parent_fwspec()

drivers/gpio/Kconfig | 1 +
drivers/gpio/gpio-pl061.c | 91 +++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 85 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
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..e95714a 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>

#define GPIODIR 0x400
#define GPIOIS 0x404
@@ -283,6 +284,64 @@ 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
+void pl061_populate_parent_fwspec(struct gpio_chip *gc,
+ struct irq_fwspec *fwspec,
+ unsigned int parent_hwirq,
+ unsigned int parent_type)
+{
+ struct device_node *dn = to_of_node(gc->irq.fwnode);
+ struct of_phandle_args pha;
+ int i;
+
+ fwspec->param_count = 0;
+
+ if (WARN_ON(!dn))
+ return;
+
+ /*
+ * 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;
+
+ if (WARN_ON(of_irq_parse_one(dn, i, &pha)))
+ return;
+
+ fwspec->param_count = pha.args_count;
+ for (i = 0; i < pha.args_count; i++)
+ fwspec->param[i] = pha.args[i];
+}
+#endif
+
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -330,16 +389,34 @@ 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]) {
+ 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;
+ } else {
+ 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_fwspec = pl061_populate_parent_fwspec;
+#endif
+ }
+
ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
if (ret)
return ret;
--
2.10.2


2021-03-18 22:20:32

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2] gpio: pl061: Support implementations without GPIOINTR line

Hi Alexander,

I love your patch! Yet something to improve:

[auto build test ERROR on gpio/for-next]
[also build test ERROR on v5.12-rc3 next-20210318]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Alexander-A-Sverdlin/gpio-pl061-Support-implementations-without-GPIOINTR-line/20210319-031352
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
config: arm-randconfig-r006-20210318 (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/bbac642db33d1e12c5a11a9630822bf16706c988
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Alexander-A-Sverdlin/gpio-pl061-Support-implementations-without-GPIOINTR-line/20210319-031352
git checkout bbac642db33d1e12c5a11a9630822bf16706c988
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All error/warnings (new ones prefixed by >>):

>> drivers/gpio/gpio-pl061.c:305:6: warning: no previous prototype for 'pl061_populate_parent_fwspec' [-Wmissing-prototypes]
305 | void pl061_populate_parent_fwspec(struct gpio_chip *gc,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/gpio/gpio-pl061.c: In function 'pl061_probe':
>> drivers/gpio/gpio-pl061.c:416:9: error: 'struct gpio_irq_chip' has no member named 'populate_parent_fwspec'; did you mean 'populate_parent_alloc_arg'?
416 | girq->populate_parent_fwspec = pl061_populate_parent_fwspec;
| ^~~~~~~~~~~~~~~~~~~~~~
| populate_parent_alloc_arg


vim +416 drivers/gpio/gpio-pl061.c

303
304 #ifdef CONFIG_OF
> 305 void pl061_populate_parent_fwspec(struct gpio_chip *gc,
306 struct irq_fwspec *fwspec,
307 unsigned int parent_hwirq,
308 unsigned int parent_type)
309 {
310 struct device_node *dn = to_of_node(gc->irq.fwnode);
311 struct of_phandle_args pha;
312 int i;
313
314 fwspec->param_count = 0;
315
316 if (WARN_ON(!dn))
317 return;
318
319 /*
320 * This brute-force here is because of the fact PL061 is often paired
321 * with GIC-v3, which has 3-cell IRQ specifier (SPI/PPI selection), and
322 * unexpected range shifts in hwirq mapping (SPI IRQs are shifted by
323 * 32). So this is about reversing of gic_irq_domain_translate().
324 */
325 for (i = 0; i < PL061_GPIO_NR; i++) {
326 unsigned int p, pt;
327
328 if (pl061_child_to_parent_hwirq(gc, i, parent_type, &p, &pt))
329 continue;
330 if (p == parent_hwirq)
331 break;
332 }
333 if (WARN_ON(i == PL061_GPIO_NR))
334 return;
335
336 if (WARN_ON(of_irq_parse_one(dn, i, &pha)))
337 return;
338
339 fwspec->param_count = pha.args_count;
340 for (i = 0; i < pha.args_count; i++)
341 fwspec->param[i] = pha.args[i];
342 }
343 #endif
344
345 static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
346 {
347 struct device *dev = &adev->dev;
348 struct pl061 *pl061;
349 struct gpio_irq_chip *girq;
350 int ret, irq;
351
352 pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);
353 if (pl061 == NULL)
354 return -ENOMEM;
355
356 pl061->base = devm_ioremap_resource(dev, &adev->res);
357 if (IS_ERR(pl061->base))
358 return PTR_ERR(pl061->base);
359
360 raw_spin_lock_init(&pl061->lock);
361 pl061->gc.request = gpiochip_generic_request;
362 pl061->gc.free = gpiochip_generic_free;
363 pl061->gc.base = -1;
364 pl061->gc.get_direction = pl061_get_direction;
365 pl061->gc.direction_input = pl061_direction_input;
366 pl061->gc.direction_output = pl061_direction_output;
367 pl061->gc.get = pl061_get_value;
368 pl061->gc.set = pl061_set_value;
369 pl061->gc.ngpio = PL061_GPIO_NR;
370 pl061->gc.label = dev_name(dev);
371 pl061->gc.parent = dev;
372 pl061->gc.owner = THIS_MODULE;
373
374 /*
375 * irq_chip support
376 */
377 pl061->irq_chip.name = dev_name(dev);
378 pl061->irq_chip.irq_ack = pl061_irq_ack;
379 pl061->irq_chip.irq_mask = pl061_irq_mask;
380 pl061->irq_chip.irq_unmask = pl061_irq_unmask;
381 pl061->irq_chip.irq_set_type = pl061_irq_type;
382 pl061->irq_chip.irq_set_wake = pl061_irq_set_wake;
383
384 writeb(0, pl061->base + GPIOIE); /* disable irqs */
385 irq = adev->irq[0];
386 if (!irq)
387 dev_warn(&adev->dev, "IRQ support disabled\n");
388 pl061->parent_irq = irq;
389
390 girq = &pl061->gc.irq;
391 girq->chip = &pl061->irq_chip;
392 girq->default_type = IRQ_TYPE_NONE;
393 girq->handler = handle_bad_irq;
394
395 /*
396 * There are some PL061 implementations which lack GPIOINTR in hardware
397 * and only have individual GPIOMIS[7:0] signals. We distinguish them by
398 * the number of IRQs assigned to the AMBA device.
399 */
400 if (!adev->irq[PL061_GPIO_NR - 1]) {
401 WARN_ON(adev->irq[1]);
402
403 girq->parent_handler = pl061_irq_handler;
404 girq->num_parents = 1;
405 girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
406 GFP_KERNEL);
407 if (!girq->parents)
408 return -ENOMEM;
409 girq->parents[0] = irq;
410 } else {
411 girq->fwnode = dev->fwnode;
412 girq->parent_domain =
413 irq_get_irq_data(adev->irq[PL061_GPIO_NR - 1])->domain;
414 girq->child_to_parent_hwirq = pl061_child_to_parent_hwirq;
415 #ifdef CONFIG_OF
> 416 girq->populate_parent_fwspec = pl061_populate_parent_fwspec;
417 #endif
418 }
419
420 ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
421 if (ret)
422 return ret;
423
424 amba_set_drvdata(adev, pl061);
425 dev_info(dev, "PL061 GPIO chip registered\n");
426
427 return 0;
428 }
429

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (6.59 kB)
.config.gz (34.48 kB)
Download all attachments

2021-03-18 22:28:32

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2] gpio: pl061: Support implementations without GPIOINTR line

Hi Alexander,

I love your patch! Perhaps something to improve:

[auto build test WARNING on gpio/for-next]
[also build test WARNING on v5.12-rc3 next-20210318]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Alexander-A-Sverdlin/gpio-pl061-Support-implementations-without-GPIOINTR-line/20210319-031352
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
config: arm-defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/bbac642db33d1e12c5a11a9630822bf16706c988
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Alexander-A-Sverdlin/gpio-pl061-Support-implementations-without-GPIOINTR-line/20210319-031352
git checkout bbac642db33d1e12c5a11a9630822bf16706c988
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

>> drivers/gpio/gpio-pl061.c:305:6: warning: no previous prototype for 'pl061_populate_parent_fwspec' [-Wmissing-prototypes]
305 | void pl061_populate_parent_fwspec(struct gpio_chip *gc,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/gpio/gpio-pl061.c: In function 'pl061_probe':
drivers/gpio/gpio-pl061.c:416:9: error: 'struct gpio_irq_chip' has no member named 'populate_parent_fwspec'; did you mean 'populate_parent_alloc_arg'?
416 | girq->populate_parent_fwspec = pl061_populate_parent_fwspec;
| ^~~~~~~~~~~~~~~~~~~~~~
| populate_parent_alloc_arg


vim +/pl061_populate_parent_fwspec +305 drivers/gpio/gpio-pl061.c

303
304 #ifdef CONFIG_OF
> 305 void pl061_populate_parent_fwspec(struct gpio_chip *gc,
306 struct irq_fwspec *fwspec,
307 unsigned int parent_hwirq,
308 unsigned int parent_type)
309 {
310 struct device_node *dn = to_of_node(gc->irq.fwnode);
311 struct of_phandle_args pha;
312 int i;
313
314 fwspec->param_count = 0;
315
316 if (WARN_ON(!dn))
317 return;
318
319 /*
320 * This brute-force here is because of the fact PL061 is often paired
321 * with GIC-v3, which has 3-cell IRQ specifier (SPI/PPI selection), and
322 * unexpected range shifts in hwirq mapping (SPI IRQs are shifted by
323 * 32). So this is about reversing of gic_irq_domain_translate().
324 */
325 for (i = 0; i < PL061_GPIO_NR; i++) {
326 unsigned int p, pt;
327
328 if (pl061_child_to_parent_hwirq(gc, i, parent_type, &p, &pt))
329 continue;
330 if (p == parent_hwirq)
331 break;
332 }
333 if (WARN_ON(i == PL061_GPIO_NR))
334 return;
335
336 if (WARN_ON(of_irq_parse_one(dn, i, &pha)))
337 return;
338
339 fwspec->param_count = pha.args_count;
340 for (i = 0; i < pha.args_count; i++)
341 fwspec->param[i] = pha.args[i];
342 }
343 #endif
344

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (3.58 kB)
.config.gz (52.57 kB)
Download all attachments