On the Renesas RZ/N1 SoC, GPIOs can generate interruptions. Those
interruption lines are multiplexed by the GPIO Interrupt Multiplexer in
order to map 32 * 3 GPIO interrupt lines to 8 GIC interrupt lines.
The GPIO interrupt multiplexer IP does nothing but select 8 GPIO
IRQ lines out of the 96 available to wire them to the GIC input lines.
Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com>
---
drivers/soc/renesas/Kconfig | 4 +
drivers/soc/renesas/Makefile | 1 +
drivers/soc/renesas/rzn1_irqmux.c | 136 ++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+)
create mode 100644 drivers/soc/renesas/rzn1_irqmux.c
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 719b7f4f376f..0878b6884515 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -58,6 +58,7 @@ config ARCH_RZN1
select PM
select PM_GENERIC_DOMAINS
select ARM_AMBA
+ select RZN1_IRQMUX
if ARM && ARCH_RENESAS
@@ -447,6 +448,9 @@ config PWC_RZV2M
config RST_RCAR
bool "Reset Controller support for R-Car" if COMPILE_TEST
+config RZN1_IRQMUX
+ bool "Renesas RZ/N1 GPIO IRQ multiplexer support" if COMPILE_TEST
+
config SYSC_RZ
bool "System controller for RZ SoCs" if COMPILE_TEST
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 3bdcc6a395d5..daa932c7698d 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -14,4 +14,5 @@ obj-$(CONFIG_SYS_R9A09G057) += r9a09g057-sys.o
# Family
obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o
obj-$(CONFIG_RST_RCAR) += rcar-rst.o
+obj-$(CONFIG_RZN1_IRQMUX) += rzn1_irqmux.o
obj-$(CONFIG_SYSC_RZ) += rz-sysc.o
diff --git a/drivers/soc/renesas/rzn1_irqmux.c b/drivers/soc/renesas/rzn1_irqmux.c
new file mode 100644
index 000000000000..1f33e357689f
--- /dev/null
+++ b/drivers/soc/renesas/rzn1_irqmux.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RZ/N1 GPIO Interrupt Multiplexer
+ *
+ * Copyright 2025 Schneider Electric
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <linux/build_bug.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/*
+ * The array index is the output line index, the value at the index is the
+ * GIC SPI interrupt number the output line is connected to.
+ */
+static u32 rzn1_irqmux_output_lines[] = {
+ 103, 104, 105, 106, 107, 108, 109, 110
+};
+
+static int rzn1_irqmux_parent_args_to_line_index(struct device *dev,
+ const struct of_phandle_args *parent_args)
+{
+ int i;
+
+ /*
+ * The parent interrupt should be one of the GIC controller.
+ * Three arguments must be provided.
+ * - args[0]: GIC_SPI
+ * - args[1]: The GIC interrupt number
+ * - args[2]: The interrupt flags
+ *
+ * We retrieve the line index based on the GIC interrupt number
+ * provided and rzn1_irqmux_output_line[] mapping array.
+ */
+
+ if (parent_args->args_count != 3 ||
+ parent_args->args[0] != GIC_SPI) {
+ dev_err(dev, "Invalid interrupt-map item\n");
+ return -EINVAL;
+ }
+
+ /* 8 output lines are available */
+ BUILD_BUG_ON(ARRAY_SIZE(rzn1_irqmux_output_lines) != 8);
+
+ for (i = 0; i < ARRAY_SIZE(rzn1_irqmux_output_lines); i++) {
+ if (parent_args->args[1] == rzn1_irqmux_output_lines[i])
+ return i;
+ }
+
+ dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]);
+ return -EINVAL;
+}
+
+static int rzn1_irqmux_setup(struct device *dev, struct device_node *np, u32 __iomem *regs)
+{
+ struct of_imap_parser imap_parser;
+ struct of_imap_item imap_item;
+ int index;
+ int ret;
+ u32 tmp;
+
+ /* We support only #interrupt-cells = <1> and #address-cells = <0> */
+ ret = of_property_read_u32(np, "#interrupt-cells", &tmp);
+ if (ret)
+ return ret;
+ if (tmp != 1)
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "#address-cells", &tmp);
+ if (ret)
+ return ret;
+ if (tmp != 0)
+ return -EINVAL;
+
+ ret = of_imap_parser_init(&imap_parser, np, &imap_item);
+ if (ret)
+ return ret;
+
+ for_each_of_imap_item(&imap_parser, &imap_item) {
+ index = rzn1_irqmux_parent_args_to_line_index(dev, &imap_item.parent_args);
+ if (index < 0) {
+ of_node_put(imap_item.parent_args.np);
+ return index;
+ }
+
+ /*
+ * The child #address-cells is 0 (already checked). The first
+ * value in imap item is the src hwirq.
+ */
+ writel(imap_item.child_imap[0], regs + index);
+ }
+
+ return 0;
+}
+
+static int rzn1_irqmux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ u32 __iomem *regs;
+ int ret;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ ret = rzn1_irqmux_setup(dev, np, regs);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to setup mux\n");
+
+ return 0;
+}
+
+static const struct of_device_id rzn1_irqmux_of_match[] = {
+ { .compatible = "renesas,rzn1-gpioirqmux", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzn1_irqmux_of_match);
+
+static struct platform_driver rzn1_irqmux_driver = {
+ .probe = rzn1_irqmux_probe,
+ .driver = {
+ .name = "rzn1_irqmux",
+ .of_match_table = rzn1_irqmux_of_match,
+ },
+};
+module_platform_driver(rzn1_irqmux_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("Renesas RZ/N1 GPIO IRQ Multiplexer Driver");
+MODULE_LICENSE("GPL");
--
2.51.0
Hi Herve, thanks for your patch! On Mon, Sep 22, 2025 at 5:27 PM Herve Codina (Schneider Electric) <herve.codina@bootlin.com> wrote: > On the Renesas RZ/N1 SoC, GPIOs can generate interruptions. Those > interruption lines are multiplexed by the GPIO Interrupt Multiplexer in > order to map 32 * 3 GPIO interrupt lines to 8 GIC interrupt lines. > > The GPIO interrupt multiplexer IP does nothing but select 8 GPIO > IRQ lines out of the 96 available to wire them to the GIC input lines. > > Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com> This looks like some complicated code to reimplement hierarchical irq domains. Can't you just select IRQ_DOMAIN_HIERARCHY and let the existing infrastructure in GPIOLIB_IRQCHIP handle this? This kind of remapping and handling is exactly what the .child_to_parent_hwirq() callback in struct gpio_irq_chip is for. This function can fail if you run out if IRQ lines. Inspect drivers/gpio/Kconfig driver that select IRQ_DOMAIN_HIERARCHY for examples of how to do this. Even if your GPIO driver is not using GPIOLIB_IRQCHIP (in that case: why not?) I think you still need to use IRQ_DOMAIN_HIERARCHY for this. Yours, Linus Walleij
Hi Linus, On Wed, 1 Oct 2025 13:08:57 +0200 Linus Walleij <linus.walleij@linaro.org> wrote: > Hi Herve, > > thanks for your patch! > > On Mon, Sep 22, 2025 at 5:27 PM Herve Codina (Schneider Electric) > <herve.codina@bootlin.com> wrote: > > > On the Renesas RZ/N1 SoC, GPIOs can generate interruptions. Those > > interruption lines are multiplexed by the GPIO Interrupt Multiplexer in > > order to map 32 * 3 GPIO interrupt lines to 8 GIC interrupt lines. > > > > The GPIO interrupt multiplexer IP does nothing but select 8 GPIO > > IRQ lines out of the 96 available to wire them to the GIC input lines. > > > > Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com> > > This looks like some complicated code to reimplement hierarchical > irq domains. > > Can't you just select IRQ_DOMAIN_HIERARCHY and let > the existing infrastructure in GPIOLIB_IRQCHIP handle > this? > > This kind of remapping and handling is exactly what the > .child_to_parent_hwirq() callback in struct gpio_irq_chip > is for. This function can fail if you run out if IRQ lines. > > Inspect drivers/gpio/Kconfig driver that select > IRQ_DOMAIN_HIERARCHY for examples of how to > do this. > > Even if your GPIO driver is not using GPIOLIB_IRQCHIP (in that > case: why not?) I think you still need to use IRQ_DOMAIN_HIERARCHY > for this. > I don't see how IRQ_DOMAIN_HIERARCHY would help. The irq-mux only muxes irq signal without performing any operations usually done by an interrupt controller. That's why I used interrupt-map in the irq-mux. The only information needed by the irq-mux is the interrupt line muxing that needs to be applied. This information is available in the interrupt-map. If we introduce IRQ_DOMAIN_HIERARCHY, either it is done at gpio controller level to route gpio interrupts to GIC interrupts and, in that case, the irq-mux is skipped and cannot apply the muxing. Or it is introduced at irq-mux level and irq-mux need to be an interrupt controller but is component is not an interrupt controller. Maybe I missed some points or I misunderstood the purpose of IRQ_DOMAIN_HIERARCHY. Can you give me some details on how IRQ_DOMAIN_HIERARCHY should be used in my case? Best regards, Hervé
On Wed, Oct 1, 2025 at 5:42 PM Herve Codina <herve.codina@bootlin.com> wrote: > On Wed, 1 Oct 2025 13:08:57 +0200 > Linus Walleij <linus.walleij@linaro.org> wrote: > I don't see how IRQ_DOMAIN_HIERARCHY would help. > > The irq-mux only muxes irq signal without performing any operations usually > done by an interrupt controller. > > That's why I used interrupt-map in the irq-mux. > > The only information needed by the irq-mux is the interrupt line muxing that > needs to be applied. This information is available in the interrupt-map. > > If we introduce IRQ_DOMAIN_HIERARCHY, either it is done at gpio controller > level to route gpio interrupts to GIC interrupts and, in that case, the > irq-mux is skipped and cannot apply the muxing. I meant to introduce the muxing code directly into the GPIO driver instead of using a separate muxing driver, using the struct gpio_irq_chip supplanted by IRQ_DOMAIN_HIERARCHY. Are these IRQ lines ever muxed for anything else than GPIO? In that case go ahead with this solution, I guess. But the title of your patch seems to suggest it is only used by GPIO. If it is only used for GPIO, why make it a separate driver instead of just putting the muxing into the GPIO driver? > Or it is introduced at irq-mux level and irq-mux need to be an interrupt > controller but is component is not an interrupt controller. It is a hierarchy, as all interrupts are routed through it. Just becaus you don't have to ACK every IRQ in the hierarchy doesn't mean it's not a hierarchy. > Maybe I missed some points or I misunderstood the purpose of > IRQ_DOMAIN_HIERARCHY. > > Can you give me some details on how IRQ_DOMAIN_HIERARCHY should be > used in my case? The gpio_irq_chip ->child_to_parent_hwirq() is called as part of the translation of each IRQ and in this callback you can set up your mux. You can return negative if you run out of muxable GPIO lines. This means the irqdomain hierarchy fits as abstraction for this usecase. Yours, Linus Walleij
Hi Linus,
giving some details about the HW until Herve can return to this topic.
> Are these IRQ lines ever muxed for anything else than
> GPIO? In that case go ahead with this solution, I guess.
> But the title of your patch seems to suggest it is
> only used by GPIO.
True, it only muxes 96 GPIOs to the 8 available irqs.
> If it is only used for GPIO, why make it a separate
> driver instead of just putting the muxing into the
> GPIO driver?
Because the HW design kind of suggests it, I'd think. The GPIO
controller is a standard Synopsis one ("snps,dw-apb-gpio") without any
extras. The GPIOMUX (which is extra) is according to the docs part of
the system controller with a dedicated set of registers. Luckily,
self-contained and not mangled with other functionality.
I am just stating where this comes from. I haven't looked into the
details of your suggestion. It may or may not make sense to add this to
the Synopsis driver with a dedicated compatible for RZ/N1D. I'll leave
this for Herve, though.
Happy hacking,
Wolfram
© 2016 - 2026 Red Hat, Inc.