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 | 150 ++++++++++++++++++++++++++++++
3 files changed, 155 insertions(+)
create mode 100644 drivers/soc/renesas/rzn1_irqmux.c
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 340a1ff7e92b..71865778b058 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -62,6 +62,7 @@ config ARCH_RZN1
select PM
select PM_GENERIC_DOMAINS
select ARM_AMBA
+ select RZN1_IRQMUX
if ARM && ARCH_RENESAS
@@ -459,6 +460,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
select MFD_SYSCON
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..42dc4174a34e
--- /dev/null
+++ b/drivers/soc/renesas/rzn1_irqmux.c
@@ -0,0 +1,150 @@
+// 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/bitops.h>
+#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 const 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,
+ const u32 output_lines[],
+ unsigned int output_lines_count)
+{
+ unsigned 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;
+ }
+
+ for (i = 0; i < output_lines_count; i++) {
+ if (parent_args->args[1] == 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;
+ unsigned long index_done = 0;
+ 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;
+
+ /* 8 output lines are available */
+ BUILD_BUG_ON(ARRAY_SIZE(rzn1_irqmux_output_lines) != 8);
+
+ /*
+ * index_done is an unsigned long integer. Be sure that no buffer
+ * overflow can occur.
+ */
+ BUILD_BUG_ON(ARRAY_SIZE(rzn1_irqmux_output_lines) > BITS_PER_LONG);
+
+ for_each_of_imap_item(&imap_parser, &imap_item) {
+ index = rzn1_irqmux_parent_args_to_line_index(dev,
+ &imap_item.parent_args,
+ rzn1_irqmux_output_lines,
+ ARRAY_SIZE(rzn1_irqmux_output_lines));
+ if (index < 0) {
+ of_node_put(imap_item.parent_args.np);
+ return index;
+ }
+
+ if (test_and_set_bit(index, &index_done)) {
+ of_node_put(imap_item.parent_args.np);
+ dev_err(dev, "Mux output line already defined\n");
+ return -EINVAL;
+ }
+
+ /*
+ * 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;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ return rzn1_irqmux_setup(dev, np, regs);
+}
+
+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 Hervé,
On Mon, 20 Oct 2025 at 10:08, 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>
Thanks for your patch!
> --- 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
One TAB too much.
> --- /dev/null
> +++ b/drivers/soc/renesas/rzn1_irqmux.c
> @@ -0,0 +1,150 @@
> +// 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/bitops.h>
> +#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 const u32 rzn1_irqmux_output_lines[] = {
> + 103, 104, 105, 106, 107, 108, 109, 110
> +};
I did read the discussion with Wolfram, but the flexibility (and
overhead) provided by this array sounds a bit overkill to me.
What about just defining:
#define RZN1_IRQMUX_SPI_BASE 103
#define RZN1_IRQMUX_NUM_IRQS 8
?
If/when a new SoC with a similar setup ever arrives, you can probably
just replace the constants above by variables inside SoC-specific
match data. And if the new mapping would be non-contiguous, you can
still revive this array ;-)
More about this below...
> +
> +static int rzn1_irqmux_parent_args_to_line_index(struct device *dev,
> + const struct of_phandle_args *parent_args,
> + const u32 output_lines[],
> + unsigned int output_lines_count)
> +{
> + unsigned 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;
> + }
> +
> + for (i = 0; i < output_lines_count; i++) {
> + if (parent_args->args[1] == output_lines[i])
> + return i;
> + }
... then this loop can be replaced by two simple comparisons.
> +
> + 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;
> + unsigned long index_done = 0;
Perhaps "DECLARE_BITMAP(index_done, RZN1_IRQMUX_NUM_IRQS)",
so the BITS_PER_LONG limit can be lifted, without any cost?
> + 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;
> +
> + /* 8 output lines are available */
> + BUILD_BUG_ON(ARRAY_SIZE(rzn1_irqmux_output_lines) != 8);
... then this check can be removed, too.
> +
> + /*
> + * index_done is an unsigned long integer. Be sure that no buffer
> + * overflow can occur.
> + */
> + BUILD_BUG_ON(ARRAY_SIZE(rzn1_irqmux_output_lines) > BITS_PER_LONG);
Currently this is less strict than the check above, so a bit useless?
> +
> + for_each_of_imap_item(&imap_parser, &imap_item) {
> + index = rzn1_irqmux_parent_args_to_line_index(dev,
> + &imap_item.parent_args,
> + rzn1_irqmux_output_lines,
> + ARRAY_SIZE(rzn1_irqmux_output_lines));
> + if (index < 0) {
> + of_node_put(imap_item.parent_args.np);
> + return index;
> + }
> +
> + if (test_and_set_bit(index, &index_done)) {
> + of_node_put(imap_item.parent_args.np);
> + dev_err(dev, "Mux output line already defined\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * 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;
> +}
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Geert,
On Tue, 21 Oct 2025 15:05:35 +0200
Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> Hi Hervé,
>
> On Mon, 20 Oct 2025 at 10:08, 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>
>
> Thanks for your patch!
>
> > --- 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
>
> One TAB too much.
Yes indeed, will be removed.
>
> > --- /dev/null
> > +++ b/drivers/soc/renesas/rzn1_irqmux.c
> > @@ -0,0 +1,150 @@
> > +// 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/bitops.h>
> > +#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 const u32 rzn1_irqmux_output_lines[] = {
> > + 103, 104, 105, 106, 107, 108, 109, 110
> > +};
>
> I did read the discussion with Wolfram, but the flexibility (and
> overhead) provided by this array sounds a bit overkill to me.
>
> What about just defining:
>
> #define RZN1_IRQMUX_SPI_BASE 103
> #define RZN1_IRQMUX_NUM_IRQS 8
>
> ?
>
> If/when a new SoC with a similar setup ever arrives, you can probably
> just replace the constants above by variables inside SoC-specific
> match data. And if the new mapping would be non-contiguous, you can
> still revive this array ;-)
I have in mind a use case that can lead to a non-contiguous mapping.
The RZ/N1 SoC embeds a Cortex-M3 CPU. This CPU can use GPIOs and
some of them for interrupt purpose. In that case, those GPIOs have
to be routed to the interrupt line expected by the Cortex-M3.
And so, we have some interrupts reserved for CPUs running Linux and
some others for the Cortex-M3.
Among those reserved interrupts may some are not used.
for instance:
Interrupt 103, 102: Reserved and used by Linux
Interrupt 103: Reserved for Linux but not used -> Hole in the mapping
Interrupt 104: Reserved and used my Cortex-M3 (need to be routed by Linux)
I don't know if this use case is relevant but I think we should be too restrictive
on the mapping and so accept holes.
With that in mind, I let you confirm that you still prefer to have a mapping
without any holes. A future patch to support that is always possible.
>
> More about this below...
>
> > +
> > +static int rzn1_irqmux_parent_args_to_line_index(struct device *dev,
> > + const struct of_phandle_args *parent_args,
> > + const u32 output_lines[],
> > + unsigned int output_lines_count)
> > +{
> > + unsigned 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;
> > + }
> > +
> > + for (i = 0; i < output_lines_count; i++) {
> > + if (parent_args->args[1] == output_lines[i])
> > + return i;
> > + }
>
> ... then this loop can be replaced by two simple comparisons.
>
> > +
> > + 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;
> > + unsigned long index_done = 0;
>
> Perhaps "DECLARE_BITMAP(index_done, RZN1_IRQMUX_NUM_IRQS)",
> so the BITS_PER_LONG limit can be lifted, without any cost?
Yes.
>
> > + 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;
> > +
> > + /* 8 output lines are available */
> > + BUILD_BUG_ON(ARRAY_SIZE(rzn1_irqmux_output_lines) != 8);
>
> ... then this check can be removed, too.
>
> > +
> > + /*
> > + * index_done is an unsigned long integer. Be sure that no buffer
> > + * overflow can occur.
> > + */
> > + BUILD_BUG_ON(ARRAY_SIZE(rzn1_irqmux_output_lines) > BITS_PER_LONG);
>
> Currently this is less strict than the check above, so a bit useless?
Yes. My first intention was to explicitly check both constraints:
- The number of output lines which lead to the maximum index value used to
index register addresses (i.e. writel(imap_item.child_imap[0], regs + index)
below).
- The number of output lines to the maximum index value used to
index register addresses which which lead to the maximum index value used
for testing and setting the "index_done" flags.
But yes, I can keep the stricter one.
Also, if RZN1_IRQMUX_NUM_IRQS is introduced and related DECLARE_BITMAP(index_done,
RZN1_IRQMUX_NUM_IRQS), a single check against RZN1_IRQMUX_NUM_IRQS will be enough
for both cases
>
> > +
> > + for_each_of_imap_item(&imap_parser, &imap_item) {
> > + index = rzn1_irqmux_parent_args_to_line_index(dev,
> > + &imap_item.parent_args,
> > + rzn1_irqmux_output_lines,
> > + ARRAY_SIZE(rzn1_irqmux_output_lines));
> > + if (index < 0) {
> > + of_node_put(imap_item.parent_args.np);
> > + return index;
> > + }
> > +
> > + if (test_and_set_bit(index, &index_done)) {
> > + of_node_put(imap_item.parent_args.np);
> > + dev_err(dev, "Mux output line already defined\n");
> > + return -EINVAL;
> > + }
> > +
> > + /*
> > + * 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;
> > +}
>
Hi Hervé,
On Wed, 22 Oct 2025 at 15:03, Herve Codina <herve.codina@bootlin.com> wrote:
> On Tue, 21 Oct 2025 15:05:35 +0200
> Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> > On Mon, 20 Oct 2025 at 10:08, 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>
> > > --- /dev/null
> > > +++ b/drivers/soc/renesas/rzn1_irqmux.c
> > > @@ -0,0 +1,150 @@
> > > +// 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/bitops.h>
> > > +#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 const u32 rzn1_irqmux_output_lines[] = {
> > > + 103, 104, 105, 106, 107, 108, 109, 110
> > > +};
> >
> > I did read the discussion with Wolfram, but the flexibility (and
> > overhead) provided by this array sounds a bit overkill to me.
> >
> > What about just defining:
> >
> > #define RZN1_IRQMUX_SPI_BASE 103
> > #define RZN1_IRQMUX_NUM_IRQS 8
> >
> > ?
> >
> > If/when a new SoC with a similar setup ever arrives, you can probably
> > just replace the constants above by variables inside SoC-specific
> > match data. And if the new mapping would be non-contiguous, you can
> > still revive this array ;-)
>
> I have in mind a use case that can lead to a non-contiguous mapping.
>
> The RZ/N1 SoC embeds a Cortex-M3 CPU. This CPU can use GPIOs and
> some of them for interrupt purpose. In that case, those GPIOs have
> to be routed to the interrupt line expected by the Cortex-M3.
>
> And so, we have some interrupts reserved for CPUs running Linux and
> some others for the Cortex-M3.
>
> Among those reserved interrupts may some are not used.
>
> for instance:
> Interrupt 103, 102: Reserved and used by Linux
> Interrupt 103: Reserved for Linux but not used -> Hole in the mapping
> Interrupt 104: Reserved and used my Cortex-M3 (need to be routed by Linux)
102 does not seem to be correct?
> I don't know if this use case is relevant but I think we should be too restrictive
> on the mapping and so accept holes.
>
> With that in mind, I let you confirm that you still prefer to have a mapping
> without any holes. A future patch to support that is always possible.
While that would indeed be a non-discontiguous mapping, I do not see how
it is related to rzn1_irqmux_output_lines[] in the driver. That array
would still contain the same contiguous values 103..110, right?
Sorry, I haven't been following the development of this driver that
closely (RZ/N1 is completely different from e.g. R-Car, and I never
had access to an RZ/N1 platform), so perhaps I am missing something.
Why does the user have to specify an interrupt-map in DT? Can't the
driver create the mapping dynamically, based actual usage of the
GPIOs? I.e. the first 8 GPIOs that ask for interrupt functionality
receive it, and are mapped to an available GIC interrupt?
I believe this is how rzg2l-irqc works, mapping up to 32 GPIO interrupts
to 32 GIC (TINT) interrupts.
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Geert,
On Thu, 23 Oct 2025 13:30:53 +0200
Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> >
> > I have in mind a use case that can lead to a non-contiguous mapping.
> >
> > The RZ/N1 SoC embeds a Cortex-M3 CPU. This CPU can use GPIOs and
> > some of them for interrupt purpose. In that case, those GPIOs have
> > to be routed to the interrupt line expected by the Cortex-M3.
> >
> > And so, we have some interrupts reserved for CPUs running Linux and
> > some others for the Cortex-M3.
> >
> > Among those reserved interrupts may some are not used.
> >
> > for instance:
> > Interrupt 103, 102: Reserved and used by Linux
> > Interrupt 103: Reserved for Linux but not used -> Hole in the mapping
> > Interrupt 104: Reserved and used my Cortex-M3 (need to be routed by Linux)
>
> 102 does not seem to be correct?
My bad, my example was wrong.
Interrupt 103, 104: Reserved and used by Linux
Interrupt 105: Reserved for Linux but not used -> Hole in the mapping
Interrupt 106: Reserved and used my Cortex-M3 (need to be routed by Linux)
>
> > I don't know if this use case is relevant but I think we should be too restrictive
> > on the mapping and so accept holes.
> >
> > With that in mind, I let you confirm that you still prefer to have a mapping
> > without any holes. A future patch to support that is always possible.
>
> While that would indeed be a non-discontiguous mapping, I do not see how
> it is related to rzn1_irqmux_output_lines[] in the driver. That array
> would still contain the same contiguous values 103..110, right?
The array rzn1_irqmux_output_lines is still contiguous yes but the mapping
defined in irq-map property no.
Looking back again at your proposal, indeed I can remove the following loop:
for (i = 0; i < output_lines_count; i++) {
if (parent_args->args[1] == output_lines[i])
return i;
}
With just
if (parent_args->args[1] >= RZN1_IRQMUX_SPI_BASE &&
parent_args->args[1] < RZN1_IRQMUX_SPI_BASE + RZN1_IRQMUX_NUM_IRQS) {
return parent_args->args[1] - RZN1_IRQMUX_SPI_BASE;
dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]);
return -EINVAL;
>
> Sorry, I haven't been following the development of this driver that
> closely (RZ/N1 is completely different from e.g. R-Car, and I never
> had access to an RZ/N1 platform), so perhaps I am missing something.
> Why does the user have to specify an interrupt-map in DT? Can't the
> driver create the mapping dynamically, based actual usage of the
> GPIOs? I.e. the first 8 GPIOs that ask for interrupt functionality
> receive it, and are mapped to an available GIC interrupt?
> I believe this is how rzg2l-irqc works, mapping up to 32 GPIO interrupts
> to 32 GIC (TINT) interrupts.
I think the main difference with rzg2l-irqc is that the RZ/N1 irq mux is
clearly not an interrupt controller.
It is just a mux with 96 inputs (GPIO lines coming from several GPIO
controller) and 8 outputs (connected to the GIC).
It is represented as an interrupt nexus node and has an interrupt-map property.
to describe the routing.
The interrupt-map property cannot be dynamically created.
Also, the routing is necessary even if the related GPIO is not used by Linux.
This GPIO can be used as a GPIO input interrupt line by the Cortex M3.
If the irq mux driver performs the routing only on Linux GPIO usage, it will
not route GPIOs depending on Cortex M3 internal usage.
Best regards,
Hervé
Hi Hervé,
On Thu, 23 Oct 2025 at 15:21, Herve Codina <herve.codina@bootlin.com> wrote:
> On Thu, 23 Oct 2025 13:30:53 +0200
> Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> > > I have in mind a use case that can lead to a non-contiguous mapping.
> > >
> > > The RZ/N1 SoC embeds a Cortex-M3 CPU. This CPU can use GPIOs and
> > > some of them for interrupt purpose. In that case, those GPIOs have
> > > to be routed to the interrupt line expected by the Cortex-M3.
> > >
> > > And so, we have some interrupts reserved for CPUs running Linux and
> > > some others for the Cortex-M3.
> > >
> > > Among those reserved interrupts may some are not used.
> > >
> > > for instance:
> > > Interrupt 103, 102: Reserved and used by Linux
> > > Interrupt 103: Reserved for Linux but not used -> Hole in the mapping
> > > Interrupt 104: Reserved and used my Cortex-M3 (need to be routed by Linux)
> >
> > 102 does not seem to be correct?
>
> My bad, my example was wrong.
> Interrupt 103, 104: Reserved and used by Linux
> Interrupt 105: Reserved for Linux but not used -> Hole in the mapping
> Interrupt 106: Reserved and used my Cortex-M3 (need to be routed by Linux)
OK, much better!
> > > I don't know if this use case is relevant but I think we should be too restrictive
> > > on the mapping and so accept holes.
> > >
> > > With that in mind, I let you confirm that you still prefer to have a mapping
> > > without any holes. A future patch to support that is always possible.
> >
> > While that would indeed be a non-discontiguous mapping, I do not see how
> > it is related to rzn1_irqmux_output_lines[] in the driver. That array
> > would still contain the same contiguous values 103..110, right?
>
> The array rzn1_irqmux_output_lines is still contiguous yes but the mapping
> defined in irq-map property no.
>
> Looking back again at your proposal, indeed I can remove the following loop:
> for (i = 0; i < output_lines_count; i++) {
> if (parent_args->args[1] == output_lines[i])
> return i;
> }
>
> With just
> if (parent_args->args[1] >= RZN1_IRQMUX_SPI_BASE &&
> parent_args->args[1] < RZN1_IRQMUX_SPI_BASE + RZN1_IRQMUX_NUM_IRQS) {
> return parent_args->args[1] - RZN1_IRQMUX_SPI_BASE;
>
> dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]);
> return -EINVAL;
Good. I like simpler code ;-)
BTW, please invert the logic, i.e. bail out early in case of error.
> > Sorry, I haven't been following the development of this driver that
> > closely (RZ/N1 is completely different from e.g. R-Car, and I never
> > had access to an RZ/N1 platform), so perhaps I am missing something.
> > Why does the user have to specify an interrupt-map in DT? Can't the
> > driver create the mapping dynamically, based actual usage of the
> > GPIOs? I.e. the first 8 GPIOs that ask for interrupt functionality
> > receive it, and are mapped to an available GIC interrupt?
> > I believe this is how rzg2l-irqc works, mapping up to 32 GPIO interrupts
> > to 32 GIC (TINT) interrupts.
>
> I think the main difference with rzg2l-irqc is that the RZ/N1 irq mux is
> clearly not an interrupt controller.
>
> It is just a mux with 96 inputs (GPIO lines coming from several GPIO
> controller) and 8 outputs (connected to the GIC).
>
> It is represented as an interrupt nexus node and has an interrupt-map property.
> to describe the routing.
>
> The interrupt-map property cannot be dynamically created.
>
> Also, the routing is necessary even if the related GPIO is not used by Linux.
> This GPIO can be used as a GPIO input interrupt line by the Cortex M3.
>
> If the irq mux driver performs the routing only on Linux GPIO usage, it will
> not route GPIOs depending on Cortex M3 internal usage.
Thanks, makes sense!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
© 2016 - 2026 Red Hat, Inc.