Hi Marc, > On Tue, 19 Feb 2019 15:27:25 +0000 > Phil Edworthy <phil.edworthy@xxxxxxxxxxx> wrote: > > > Hello, > > > > Any comments on this patch? > > Err... I'm afraid it fell through the cracks. It's been three months, and I've > paged out most of last year. > > Can you please resend it on top of a recent -rc, and I'll try to have a look. No > promise though. Thanks, appreciate it you can! Phil > > Thanks, > > M. > > > > > Thanks > > Phil > > > > > -----Original Message----- > > > From: Phil Edworthy <phil.edworthy@xxxxxxxxxxx> > > > Sent: 13 November 2018 13:09 > > > To: Marc Zyngier <marc.zyngier@xxxxxxx>; Thomas Gleixner > > > <tglx@xxxxxxxxxxxxx>; Jason Cooper <jason@xxxxxxxxxxxxxx> > > > Cc: Geert Uytterhoeven <geert@xxxxxxxxxxxxxx>; linux-renesas- > > > soc@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; Phil Edworthy > > > <phil.edworthy@xxxxxxxxxxx> > > > Subject: [PATCH v3 2/2] irqchip: Add support for Renesas RZ/N1 GPIO > > > interrupt multiplexer > > > > > > On RZ/N1 devices, there are 3 Synopsys DesignWare GPIO blocks each > > > configured to have 32 interrupt outputs, so we have a total of 96 > > > GPIO interrupts. All of these are passed to the GPIO IRQ Muxer, > > > which selects > > > 8 of the GPIO interrupts to pass onto the GIC. The interrupt signals > > > aren't latched, so there is nothing to do in this driver when an > > > interrupt is received, other than tell the corresponding GPIO block. > > > > > > Signed-off-by: Phil Edworthy <phil.edworthy@xxxxxxxxxxx> > > > --- > > > v3: > > > - Use 'interrupt-map' DT property to map the interrupts, this is very > similar > > > to PCIe MSI. The only difference is that we need to get hold of the > interrupt > > > specifier for the interupts coming into the irqmux. > > > - Do not use a chained interrupt controller. > > > v2: > > > - Use interrupt-map to allow the GPIO controller info to be specified > > > as part of the irq. > > > - Renamed struct and funcs from 'girq' to a more comprehenisble > 'irqmux'. > > > --- > > > drivers/irqchip/Kconfig | 9 ++ > > > drivers/irqchip/Makefile | 1 + > > > drivers/irqchip/rzn1-irq-mux.c | 205 > > > +++++++++++++++++++++++++++++++++ > > > 3 files changed, 215 insertions(+) > > > create mode 100644 drivers/irqchip/rzn1-irq-mux.c > > > > > > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index > > > 96451b581452..53c54bba1dd8 100644 > > > --- a/drivers/irqchip/Kconfig > > > +++ b/drivers/irqchip/Kconfig > > > @@ -204,6 +204,15 @@ config RENESAS_IRQC > > > select GENERIC_IRQ_CHIP > > > select IRQ_DOMAIN > > > > > > +config RENESAS_RZN1_IRQ_MUX > > > + bool "Renesas RZ/N1 GPIO IRQ multiplexer support" > > > + depends on ARCH_RZN1 > > > + select IRQ_DOMAIN > > > + help > > > + Say yes here to add support for the GPIO IRQ multiplexer > > > embedded > > > + in Renesas RZ/N1 SoC devices. The GPIO IRQ Muxer selects which of > > > + the interrupts coming from the GPIO controllers are used. > > > + > > > config ST_IRQCHIP > > > bool > > > select REGMAP > > > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile > > > index b822199445ff..b090f84dd42e 100644 > > > --- a/drivers/irqchip/Makefile > > > +++ b/drivers/irqchip/Makefile > > > @@ -45,6 +45,7 @@ obj-$(CONFIG_SIRF_IRQ) += > irq- > > > sirfsoc.o > > > obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o > > > obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o > > > obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o > > > +obj-$(CONFIG_RENESAS_RZN1_IRQ_MUX) += rzn1-irq-mux.o > > > obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o > > > obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o > > > obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o > > > diff --git a/drivers/irqchip/rzn1-irq-mux.c > > > b/drivers/irqchip/rzn1-irq-mux.c new file mode 100644 index > > > 000000000000..ee7810b9b3f3 > > > --- /dev/null > > > +++ b/drivers/irqchip/rzn1-irq-mux.c > > > @@ -0,0 +1,205 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * RZ/N1 GPIO Interrupt Multiplexer > > > + * > > > + * Copyright (C) 2018 Renesas Electronics Europe Limited > > > + * > > > + * On RZ/N1 devices, there are 3 Synopsys DesignWare GPIO blocks > > > +each configured > > > + * to have 32 interrupt outputs, so we have a total of 96 GPIO interrupts. > > > + * All of these are passed to the GPIO IRQ Muxer, which selects 8 > > > +of the GPIO > > > + * interrupts to pass onto the GIC. > > > + */ > > > + > > > +#include <linux/interrupt.h> > > > +#include <linux/irq.h> > > > +#include <linux/irqdomain.h> > > > +#include <linux/kernel.h> > > > +#include <linux/module.h> > > > +#include <linux/of_irq.h> > > > +#include <linux/of_platform.h> > > > + > > > +#define MAX_NR_INPUT_IRQS 96 > > > +#define MAX_NR_OUTPUT_IRQS 8 > > > + > > > +/* > > > + * "interrupt-map" consists of 1 interrupt cell, 0 address cells, > > > +phandle to > > > + * interrupt parent, and parent interrupt specifier (3 cells for > > > +GIC), giving > > > + * a total of 5 cells. > > > + */ > > > +#define IMAP_LENGTH 5 > > > + > > > +struct irqmux_priv; > > > +struct irqmux_one { > > > + unsigned int irq; > > > + unsigned int src_hwirq; > > > + struct irqmux_priv *priv; > > > +}; > > > + > > > +struct irqmux_priv { > > > + struct device *dev; > > > + struct irq_domain *irq_domain; > > > + unsigned int nr_irqs; > > > + struct irqmux_one mux[MAX_NR_OUTPUT_IRQS]; }; > > > + > > > +static irqreturn_t irqmux_handler(int irq, void *data) { > > > + struct irqmux_one *mux = data; > > > + struct irqmux_priv *priv = mux->priv; > > > + unsigned int virq; > > > + > > > + virq = irq_find_mapping(priv->irq_domain, mux->src_hwirq); > > > + > > > + generic_handle_irq(virq); > > > + > > > + return IRQ_HANDLED; > > > +} > > > + > > > +static int irqmux_domain_map(struct irq_domain *h, unsigned int irq, > > > + irq_hw_number_t hwirq) > > > +{ > > > + irq_set_chip_data(irq, h->host_data); > > > + irq_set_chip_and_handler(irq, &dummy_irq_chip, > > > handle_simple_irq); > > > + > > > + return 0; > > > +} > > > + > > > +static const struct irq_domain_ops irqmux_domain_ops = { > > > + .map = irqmux_domain_map, > > > +}; > > > + > > > +static int irqmux_probe(struct platform_device *pdev) { > > > + struct device *dev = &pdev->dev; > > > + struct device_node *np = dev->of_node; > > > + struct resource *res; > > > + u32 __iomem *regs; > > > + struct irqmux_priv *priv; > > > + unsigned int i; > > > + int nr_irqs; > > > + int ret; > > > + const __be32 *imap; > > > + int imaplen; > > > + > > > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > > > + if (!priv) > > > + return -ENOMEM; > > > + > > > + priv->dev = dev; > > > + platform_set_drvdata(pdev, priv); > > > + > > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > > + regs = devm_ioremap_resource(dev, res); > > > + if (IS_ERR(regs)) > > > + return PTR_ERR(regs); > > > + > > > + nr_irqs = of_irq_count(np); > > > + if (nr_irqs < 0) > > > + return nr_irqs; > > > + > > > + if (nr_irqs > MAX_NR_OUTPUT_IRQS) { > > > + dev_err(dev, "too many output interrupts\n"); > > > + return -ENOENT; > > > + } > > > + > > > + priv->nr_irqs = nr_irqs; > > > + > > > + /* Look for the interrupt-map */ > > > + imap = of_get_property(np, "interrupt-map", &imaplen); > > > + if (!imap) > > > + return -ENOENT; > > > + imaplen /= IMAP_LENGTH * sizeof(__be32); > > > + > > > + /* Sometimes not all muxs are used */ > > > + if (imaplen < priv->nr_irqs) > > > + priv->nr_irqs = imaplen; > > > + > > > + /* Create IRQ domain for the interrupts coming from the GPIO > > > +blocks > > > */ > > > + priv->irq_domain = irq_domain_add_linear(np, > > > MAX_NR_INPUT_IRQS, > > > + &irqmux_domain_ops, priv); > > > + if (!priv->irq_domain) > > > + return -ENOMEM; > > > + > > > + for (i = 0; i < MAX_NR_INPUT_IRQS; i++) > > > + irq_create_mapping(priv->irq_domain, i); > > > + > > > + for (i = 0; i < priv->nr_irqs; i++) { > > > + struct irqmux_one *mux = &priv->mux[i]; > > > + > > > + ret = irq_of_parse_and_map(np, i); > > > + if (ret < 0) { > > > + ret = -ENOENT; > > > + goto err; > > > + } > > > + > > > + mux->irq = ret; > > > + mux->priv = priv; > > > + > > > + /* > > > + * We need the first cell of the interrupt-map to configure > > > + * the hardware. > > > + */ > > > + mux->src_hwirq = be32_to_cpu(*imap); > > > + imap += IMAP_LENGTH; > > > + > > > + dev_info(dev, "%u: %u mapped irq %u\n", i, mux- > > > >src_hwirq, > > > + mux->irq); > > > + > > > + ret = devm_request_irq(dev, mux->irq, irqmux_handler, > > > + IRQF_SHARED | IRQF_NO_THREAD, > > > + "irqmux", mux); > > > + if (ret < 0) { > > > + dev_err(dev, "failed to request IRQ: %d\n", ret); > > > + goto err; > > > + } > > > + > > > + /* Set up the hardware to pass the interrupt through */ > > > + writel(mux->src_hwirq, ®s[i]); > > > + } > > > + > > > + dev_info(dev, "probed, %d gpio interrupts\n", priv->nr_irqs); > > > + > > > + return 0; > > > + > > > +err: > > > + while (i--) > > > + irq_dispose_mapping(priv->mux[i].irq); > > > + irq_domain_remove(priv->irq_domain); > > > + > > > + return ret; > > > +} > > > + > > > +static int irqmux_remove(struct platform_device *pdev) { > > > + struct irqmux_priv *priv = platform_get_drvdata(pdev); > > > + unsigned int i; > > > + > > > + for (i = 0; i < priv->nr_irqs; i++) > > > + irq_dispose_mapping(priv->mux[i].irq); > > > + irq_domain_remove(priv->irq_domain); > > > + > > > + return 0; > > > +} > > > + > > > +static const struct of_device_id irqmux_match[] = { > > > + { .compatible = "renesas,rzn1-gpioirqmux", }, > > > + { /* sentinel */ }, > > > +}; > > > + > > > +MODULE_DEVICE_TABLE(of, irqmux_match); > > > + > > > +static struct platform_driver irqmux_driver = { > > > + .driver = { > > > + .name = "gpio_irq_mux", > > > + .owner = THIS_MODULE, > > > + .of_match_table = irqmux_match, > > > + }, > > > + .probe = irqmux_probe, > > > + .remove = irqmux_remove, > > > +}; > > > + > > > +module_platform_driver(irqmux_driver); > > > + > > > +MODULE_DESCRIPTION("Renesas RZ/N1 GPIO IRQ Multiplexer Driver"); > > > +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@xxxxxxxxxxx>"); > > > +MODULE_LICENSE("GPL v2"); > > > -- > > > 2.17.1 > > > > > > -- > Without deviation from the norm, progress is not possible.