[PATCH 2/3] pinctrl: s32cc: add GPIO external interrupt (EIRQ) support

From: Khristine Andreea Barbulescu

Date: Thu Oct 08 2026 - 03:20:00 EST


Add SIUL2 external interrupt support to the S32CC pinctrl driver so
that EIRQ-capable GPIO lines can be used as EIRQs.

The driver maps the SIUL2 EIRQ register window (DISR0/DIRER0/
IREER0/IFEER0), creates a linear IRQ domain over the GPIO line space,
and registers a chained handler on the parent GIC interrupt. An
irqchip implements mask/unmask (DIRER0), ack (write-1-to-clear DISR0)
and set_type (rising/falling/both via IREER0/IFEER0).

Because the EIRQ lines are not hard-wired to fixed pads, each line has
its own input multiplexer (IMCR). Routing is described by a per-SoC
pad-to-EIRQ table (gpio, eirq, imcr_pin, sss). The pad is routed into
the EIRQ input mux once at irq request time (and disconnected at
release); mask/unmask only toggle the line enable (DIRER0) and clear
pending status (DISR0). Routing the IMCR SSS field just once keeps the
edge-detector input stable across mask/unmask cycles; reconnecting it
on every unmask would re-drive a level transition into the detector and
latch a spurious extra event.

The S32G data adds the EIRQ0..EIRQ31 routing table, exposes the EIRQ
input-mux IMCR pins (pin IDs 910..941 / IMCR398..IMCR429) as a pin
range so SSS writes can be resolved, and wires up eirq_data.

Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@xxxxxxxxxxx>
---
drivers/pinctrl/nxp/pinctrl-s32.h | 32 +++
drivers/pinctrl/nxp/pinctrl-s32cc.c | 406 ++++++++++++++++++++++++++++
drivers/pinctrl/nxp/pinctrl-s32g2.c | 72 +++++
3 files changed, 510 insertions(+)

diff --git a/drivers/pinctrl/nxp/pinctrl-s32.h b/drivers/pinctrl/nxp/pinctrl-s32.h
index 028578a090e4..2a1ef719c7da 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32.h
+++ b/drivers/pinctrl/nxp/pinctrl-s32.h
@@ -61,6 +61,37 @@ struct s32_gpio_pad_map {
unsigned int pad;
};

+/**
+ * struct s32_eirq_line - one pad-to-EIRQ routing option
+ * @gpio: GPIO line offset that can drive this EIRQ line
+ * @eirq: EIRQ hardware line index (bit position in the DISR0/DIRER0/
+ * IREER0/IFEER0 registers)
+ * @imcr_pin: pinctrl pin id of the EIRQ IMCR that
+ * routes the pad signal into the SIUL2 edge detector
+ * @sss: Source Signal Select value to program into @imcr_pin so that the
+ * pad backing @gpio is connected to the EIRQ input
+ *
+ */
+struct s32_eirq_line {
+ unsigned int gpio;
+ unsigned int eirq;
+ unsigned int imcr_pin;
+ unsigned int sss;
+};
+
+/**
+ * struct s32_eirq_data - EIRQ capability description
+ * @num_eirqs: number of EIRQ hardware lines exposed by the SoC (the number
+ * of usable bits in the DISR0/DIRER0/IREER0/IFEER0 registers)
+ * @num_lines: number of pad-to-EIRQ routing entries in @lines
+ * @lines: array describing every pad that can be routed to an EIRQ line
+ */
+struct s32_eirq_data {
+ unsigned int num_eirqs;
+ unsigned int num_lines;
+ const struct s32_eirq_line *lines;
+};
+
struct s32_pinctrl_soc_data {
const struct pinctrl_pin_desc *pins;
unsigned int npins;
@@ -70,6 +101,7 @@ struct s32_pinctrl_soc_data {
unsigned int num_gpio_ranges;
const struct s32_gpio_pad_map *gpio_pad_maps;
unsigned int num_gpio_pad_maps;
+ const struct s32_eirq_data *eirq_data;
};

struct s32_pinctrl_soc_info {
diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
index 0025add68495..ecdc2a8e3fa6 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
+++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
@@ -8,11 +8,15 @@
*/

#include <linux/bitops.h>
+#include <linux/bsearch.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -40,6 +44,16 @@
#define S32_MSCR_ODE BIT(20)
#define S32_MSCR_OBE BIT(21)

+/*
+ * SIUL2 External Interrupt (EIRQ) register offsets, relative to the
+ * EIRQ memory region (DISR0). Only the DMA/Interrupt registers needed
+ * for managing GPIO interrupts are described here.
+ */
+#define S32_EIRQ_DISR0 0x00
+#define S32_EIRQ_DIRER0 0x08
+#define S32_EIRQ_IREER0 0x18
+#define S32_EIRQ_IFEER0 0x20
+
#define S32_GPIO_OP_SHIFT 16
#define S32_GPIO_OP_MASK GENMASK(19, 16)

@@ -153,6 +167,14 @@ struct s32_pinctrl_context {
* @gpio_names: GPIO line names array passed to gpio-regmap
* @gpio_configs: saved configurations for GPIO pins
* @gpio_configs_lock: lock for the `gpio_configs` list
+ * @eirq_map: regmap for the SIUL2 EIRQ register window
+ * @eirq_irq: parent (GIC) interrupt feeding all EIRQ lines
+ * @eirq_domain: IRQ domain exposed to GPIO consumers for EIRQ-capable pins
+ * @eirq_lock: protects RMW accesses to the shared EIRQ registers
+ * @eirq_owner: GPIO line currently routed to each EIRQ hardware line, or -1
+ * when the line is free. Only one pad can drive a given EIRQ
+ * line at a time because they share the same IMCR SSS field.
+ * @eirq_present: true once the EIRQ hardware has been probed successfully
* @saved_context: configuration saved over system sleep
*/
struct s32_pinctrl {
@@ -168,6 +190,12 @@ struct s32_pinctrl {
const char *const *gpio_names;
struct list_head gpio_configs;
spinlock_t gpio_configs_lock;
+ struct regmap *eirq_map;
+ int eirq_irq;
+ struct irq_domain *eirq_domain;
+ raw_spinlock_t eirq_lock;
+ int *eirq_owner;
+ bool eirq_present;
#ifdef CONFIG_PM_SLEEP
struct s32_pinctrl_context saved_context;
#endif
@@ -1450,6 +1478,373 @@ static int s32_pinctrl_probe_dt(struct platform_device *pdev,
return 0;
}

+static int s32_eirq_line_cmp(const void *key, const void *elem)
+{
+ const struct s32_eirq_line *line = elem;
+ unsigned int gpio = *(const unsigned int *)key;
+
+ if (gpio < line->gpio)
+ return -1;
+ if (gpio > line->gpio)
+ return 1;
+ return 0;
+}
+
+/*
+ * Find the pad-to-EIRQ routing entry for a given GPIO line offset.
+ */
+static const struct s32_eirq_line *
+s32_eirq_find(struct s32_pinctrl *ipctl, irq_hw_number_t hwirq)
+{
+ const struct s32_eirq_data *data = ipctl->info->soc_data->eirq_data;
+ unsigned int gpio = hwirq;
+
+ if (!ipctl->eirq_present || !data)
+ return NULL;
+
+ return bsearch(&gpio, data->lines, data->num_lines,
+ sizeof(*data->lines), s32_eirq_line_cmp);
+}
+
+/*
+ * Route or disconnect the pad backing an EIRQ line by
+ * programming the SSS field of the EIRQ IMCR.
+ * Writing the route value selects the pad as the EIRQ source; writing 0
+ * disconnects it. The IMCR lives in the normal MSCR/IMCR register space,
+ * so use the pinctrl regmap helper rather than the EIRQ window.
+ */
+static int s32_eirq_set_route(struct s32_pinctrl *ipctl,
+ const struct s32_eirq_line *line, bool connect)
+{
+ int ret;
+
+ ret = s32_regmap_update(ipctl->pctl, line->imcr_pin, S32_MSCR_SSS_MASK,
+ connect ? line->sss : 0);
+ if (ret)
+ dev_err(ipctl->dev,
+ "Failed to %s EIRQ%u input mux (IMCR pin %u): %d\n",
+ connect ? "route" : "unroute", line->eirq,
+ line->imcr_pin, ret);
+
+ return ret;
+}
+
+/*
+ * Claim the shared EIRQ line for this GPIO and connect its pad to the edge
+ * detector. Called once from the IRQ core when the interrupt is requested
+ * (free_irq() runs the release counterpart), NOT on every mask/unmask.
+ *
+ * Routing must happen here rather than in unmask() because unmask() runs on
+ * every delivered edge (handle_edge_irq does mask -> ack -> handler ->
+ * unmask). Reconnecting the IMCR input while the pad is still asserted drives
+ * a fresh level transition into the edge detector, which latches a spurious
+ * second event in DISR0 and delivers a phantom interrupt. Programming the
+ * route just once keeps the input stable across mask/unmask cycles.
+ */
+static int s32_gpio_irq_request_resources(struct irq_data *d)
+{
+ struct s32_pinctrl *ipctl = irq_data_get_irq_chip_data(d);
+ const struct s32_eirq_line *line = s32_eirq_find(ipctl, irqd_to_hwirq(d));
+ int owner, ret;
+
+ if (!line)
+ return -EINVAL;
+
+ /*
+ * Several pads can be muxed to the same EIRQ hardware line, but they
+ * all share a single IMCR SSS field, so only one pad can drive a given
+ * EIRQ line at a time. Routing a second pad would silently overwrite
+ * the SSS of the first and drop its interrupts, so reject the request
+ * when the line is already claimed by a different GPIO.
+ */
+ scoped_guard(raw_spinlock_irqsave, &ipctl->eirq_lock) {
+ owner = ipctl->eirq_owner[line->eirq];
+ if (owner != -1 && owner != line->gpio) {
+ dev_err(ipctl->dev,
+ "EIRQ%u already used by GPIO %d, cannot enable GPIO %u\n",
+ line->eirq, owner, line->gpio);
+ return -EBUSY;
+ }
+ ipctl->eirq_owner[line->eirq] = line->gpio;
+ }
+
+ /* Route the pad into the EIRQ input mux once, up front. */
+ ret = s32_eirq_set_route(ipctl, line, true);
+ if (ret) {
+ scoped_guard(raw_spinlock_irqsave, &ipctl->eirq_lock)
+ if (ipctl->eirq_owner[line->eirq] == line->gpio)
+ ipctl->eirq_owner[line->eirq] = -1;
+ }
+
+ return ret;
+}
+
+/* Disconnect the pad and release the shared EIRQ line (free_irq path). */
+static void s32_gpio_irq_release_resources(struct irq_data *d)
+{
+ struct s32_pinctrl *ipctl = irq_data_get_irq_chip_data(d);
+ const struct s32_eirq_line *line = s32_eirq_find(ipctl, irqd_to_hwirq(d));
+
+ if (!line)
+ return;
+
+ /* Disable the line and disconnect the pad from the EIRQ input mux. */
+ scoped_guard(raw_spinlock_irqsave, &ipctl->eirq_lock)
+ regmap_update_bits(ipctl->eirq_map, S32_EIRQ_DIRER0,
+ BIT(line->eirq), 0);
+
+ s32_eirq_set_route(ipctl, line, false);
+
+ scoped_guard(raw_spinlock_irqsave, &ipctl->eirq_lock)
+ if (ipctl->eirq_owner[line->eirq] == line->gpio)
+ ipctl->eirq_owner[line->eirq] = -1;
+}
+
+/*
+ * Enable or disable an EIRQ line through DIRER0. The IMCR route stays
+ * connected across mask/unmask (it is programmed once in
+ * request_resources) so that re-enabling the line does not fabricate a
+ * spurious edge. When unmasking, first discard any edge latched while the
+ * line was masked so a stale flag does not deliver an extra interrupt; the
+ * status flags are write-1-to-clear.
+ */
+static void s32_gpio_irq_set_mask(struct irq_data *d, bool mask)
+{
+ struct s32_pinctrl *ipctl = irq_data_get_irq_chip_data(d);
+ const struct s32_eirq_line *line = s32_eirq_find(ipctl, irqd_to_hwirq(d));
+
+ if (!line)
+ return;
+
+ scoped_guard(raw_spinlock_irqsave, &ipctl->eirq_lock) {
+ if (!mask)
+ regmap_write(ipctl->eirq_map, S32_EIRQ_DISR0,
+ BIT(line->eirq));
+ regmap_update_bits(ipctl->eirq_map, S32_EIRQ_DIRER0,
+ BIT(line->eirq), mask ? 0 : BIT(line->eirq));
+ }
+}
+
+static void s32_gpio_irq_mask(struct irq_data *d)
+{
+ s32_gpio_irq_set_mask(d, true);
+}
+
+static void s32_gpio_irq_unmask(struct irq_data *d)
+{
+ s32_gpio_irq_set_mask(d, false);
+}
+
+static void s32_gpio_irq_ack(struct irq_data *d)
+{
+ struct s32_pinctrl *ipctl = irq_data_get_irq_chip_data(d);
+ const struct s32_eirq_line *line = s32_eirq_find(ipctl, irqd_to_hwirq(d));
+
+ if (!line)
+ return;
+
+ /* The status flags are write-1-to-clear. */
+ regmap_write(ipctl->eirq_map, S32_EIRQ_DISR0, BIT(line->eirq));
+}
+
+static int s32_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct s32_pinctrl *ipctl = irq_data_get_irq_chip_data(d);
+ const struct s32_eirq_line *line;
+ u32 mask, ireer = 0, ifeer = 0;
+
+ line = s32_eirq_find(ipctl, irqd_to_hwirq(d));
+ if (!line)
+ return -EINVAL;
+
+ mask = BIT(line->eirq);
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ ireer = mask;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ ifeer = mask;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ ireer = mask;
+ ifeer = mask;
+ break;
+ default:
+ /* The EIRQ hardware only supports edge-triggered events. */
+ return -EINVAL;
+ }
+
+ scoped_guard(raw_spinlock_irqsave, &ipctl->eirq_lock) {
+ regmap_update_bits(ipctl->eirq_map, S32_EIRQ_IREER0, mask, ireer);
+ regmap_update_bits(ipctl->eirq_map, S32_EIRQ_IFEER0, mask, ifeer);
+ }
+
+ irq_set_handler_locked(d, handle_edge_irq);
+
+ return 0;
+}
+
+static const struct irq_chip s32_gpio_irq_chip = {
+ .name = "s32-gpio-eirq",
+ .irq_ack = s32_gpio_irq_ack,
+ .irq_mask = s32_gpio_irq_mask,
+ .irq_unmask = s32_gpio_irq_unmask,
+ .irq_set_type = s32_gpio_irq_set_type,
+ .irq_request_resources = s32_gpio_irq_request_resources,
+ .irq_release_resources = s32_gpio_irq_release_resources,
+ .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
+};
+
+
+static void s32_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct s32_pinctrl *ipctl = irq_desc_get_handler_data(desc);
+ const struct s32_eirq_data *data = ipctl->info->soc_data->eirq_data;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned long pending;
+ u32 disr, direr;
+ int eirq;
+
+ chained_irq_enter(chip, desc);
+
+ regmap_read(ipctl->eirq_map, S32_EIRQ_DISR0, &disr);
+ regmap_read(ipctl->eirq_map, S32_EIRQ_DIRER0, &direr);
+ pending = disr & direr;
+
+ for_each_set_bit(eirq, &pending, data->num_eirqs) {
+ int gpio;
+
+ /*
+ * Translate the EIRQ hardware line back to the GPIO line
+ * offset that is currently routed to it. Several pads may
+ * map to the same EIRQ line, but only one pad can be routed
+ * to it at a time (they share a single IMCR SSS field), so
+ * dispatch solely to the GPIO that currently owns the line.
+ */
+ scoped_guard(raw_spinlock, &ipctl->eirq_lock)
+ gpio = ipctl->eirq_owner[eirq];
+
+ if (gpio != -1) {
+ generic_handle_domain_irq(ipctl->eirq_domain, gpio);
+ } else {
+ /*
+ * A pending, enabled EIRQ line with no owner should
+ * not happen. Clear it to avoid an interrupt storm and
+ * flag the unexpected condition.
+ */
+ regmap_write(ipctl->eirq_map, S32_EIRQ_DISR0, BIT(eirq));
+ dev_err_ratelimited(ipctl->dev,
+ "Spurious EIRQ%d with no owner\n",
+ eirq);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int s32_eirq_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct s32_pinctrl *ipctl = d->host_data;
+
+ /* Only GPIO lines wired to an EIRQ line can be used as interrupts. */
+ if (!s32_eirq_find(ipctl, hwirq))
+ return -EINVAL;
+
+ irq_set_chip_data(virq, ipctl);
+ irq_set_chip_and_handler(virq, &s32_gpio_irq_chip, handle_edge_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops s32_eirq_domain_ops = {
+ .map = s32_eirq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static void s32_eirq_teardown(void *data)
+{
+ struct s32_pinctrl *ipctl = data;
+
+ irq_set_chained_handler_and_data(ipctl->eirq_irq, NULL, NULL);
+ irq_domain_remove(ipctl->eirq_domain);
+}
+
+static int s32_pinctrl_init_eirq(struct platform_device *pdev,
+ struct s32_pinctrl *ipctl)
+{
+ const struct s32_pinctrl_soc_data *soc = ipctl->info->soc_data;
+ struct regmap_config cfg;
+ unsigned int eirq_idx;
+ void __iomem *base;
+ unsigned int i;
+
+ cfg = (struct regmap_config){
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .name = "eirq",
+ .max_register = S32_EIRQ_IFEER0,
+ .fast_io = true,
+ };
+
+ ipctl->eirq_present = false;
+
+ if (!soc->eirq_data)
+ return 0;
+
+ raw_spin_lock_init(&ipctl->eirq_lock);
+
+ /* Track which GPIO currently owns each EIRQ line (-1 == free). */
+ ipctl->eirq_owner = devm_kcalloc(&pdev->dev, soc->eirq_data->num_eirqs,
+ sizeof(*ipctl->eirq_owner),
+ GFP_KERNEL);
+ if (!ipctl->eirq_owner)
+ return -ENOMEM;
+
+ for (i = 0; i < soc->eirq_data->num_eirqs; i++)
+ ipctl->eirq_owner[i] = -1;
+
+ /*
+ * EIRQ registers are placed after the pinctrl regions and the
+ * per-range PGPDO/PGPDI GPIO regions.
+ */
+ eirq_idx = soc->mem_regions + soc->num_gpio_ranges * 2;
+
+ base = devm_platform_ioremap_resource(pdev, eirq_idx);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ ipctl->eirq_map = devm_regmap_init_mmio(&pdev->dev, base, &cfg);
+ if (IS_ERR(ipctl->eirq_map))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ipctl->eirq_map),
+ "Failed to init EIRQ regmap\n");
+
+ ipctl->eirq_irq = platform_get_irq(pdev, 0);
+ if (ipctl->eirq_irq < 0)
+ return ipctl->eirq_irq;
+
+ /* Mask all EIRQ lines and clear any pending status before use. */
+ regmap_write(ipctl->eirq_map, S32_EIRQ_DIRER0, 0);
+ regmap_write(ipctl->eirq_map, S32_EIRQ_DISR0, GENMASK(31, 0));
+
+ ipctl->eirq_domain = irq_domain_create_linear(dev_fwnode(&pdev->dev),
+ ipctl->ngpio,
+ &s32_eirq_domain_ops,
+ ipctl);
+ if (!ipctl->eirq_domain)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "Failed to create EIRQ IRQ domain\n");
+
+ ipctl->eirq_present = true;
+
+ irq_set_chained_handler_and_data(ipctl->eirq_irq,
+ s32_gpio_irq_handler, ipctl);
+
+ return devm_add_action_or_reset(&pdev->dev, s32_eirq_teardown, ipctl);
+}
+
int s32_pinctrl_probe(struct platform_device *pdev,
const struct s32_pinctrl_soc_data *soc_data)
{
@@ -1553,6 +1948,15 @@ int s32_pinctrl_probe(struct platform_device *pdev,
if (ret)
return ret;

+ /*
+ * Set up EIRQ support before registering the GPIO chip so the EIRQ
+ * IRQ domain can be handed over to gpio-regmap. The IRQ domain is
+ * sized over the GPIO line space, so ngpio must already be known.
+ */
+ ret = s32_pinctrl_init_eirq(pdev, ipctl);
+ if (ret)
+ return ret;
+
gpio_cfg.parent = &pdev->dev;
gpio_cfg.fwnode = dev_fwnode(&pdev->dev);
gpio_cfg.label = dev_name(&pdev->dev);
@@ -1565,6 +1969,8 @@ int s32_pinctrl_probe(struct platform_device *pdev,
gpio_cfg.reg_mask_xlate = s32_gpio_reg_mask_xlate;
gpio_cfg.init_valid_mask = s32_init_valid_mask;
gpio_cfg.drvdata = ipctl;
+ if (ipctl->eirq_present)
+ gpio_cfg.irq_domain = ipctl->eirq_domain;

ipctl->gpio_rgm = devm_gpio_regmap_register(&pdev->dev, &gpio_cfg);
if (IS_ERR(ipctl->gpio_rgm))
diff --git a/drivers/pinctrl/nxp/pinctrl-s32g2.c b/drivers/pinctrl/nxp/pinctrl-s32g2.c
index f9546c67a269..5d08a871220c 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32g2.c
+++ b/drivers/pinctrl/nxp/pinctrl-s32g2.c
@@ -770,6 +770,15 @@ static const struct s32_pin_range s32_pin_ranges_siul2[] = {
/* IMCR pin ID ranges */
S32_PIN_RANGE(512, 595),
S32_PIN_RANGE(631, 909),
+ /*
+ * IMCR398..IMCR429 (pin IDs 910..941) are the SIUL2 external
+ * interrupt (EIRQ0..EIRQ31) input multiplexers. They sit in a gap
+ * between the two SIUL2_1 IMCR windows above/below. Without this
+ * range s32_get_region() cannot resolve the EIRQ IMCR pins, so
+ * routing a pad to an EIRQ line (SSS write) silently fails and the
+ * interrupt never fires.
+ */
+ S32_PIN_RANGE(910, 941),
S32_PIN_RANGE(942, 1007),
};

@@ -793,6 +802,68 @@ static const struct s32_gpio_pad_map s32g_gpio_pad_maps[] = {
{ 176, 190, 11 }, /* PL_00 .. PL_14 -> PGPD11 */
};

+/*
+ * Pad to EIRQ routing table.
+ *
+ * On S32G2/S32G3 the SIUL2 external interrupt lines (EIRQ0..EIRQ31) are
+ * NOT hard-wired to fixed pads. Each EIRQ line has its own input
+ * multiplexer (an IMCR register, S32G_IMCR_SIUL_EIRQ<n>) whose Source
+ * Signal Select (SSS) field chooses which pad signal feeds the edge
+ * detector. The same EIRQ line can be sourced from more than one pad,
+ * so several entries may share an .eirq value.
+ */
+static const struct s32_eirq_line s32g_eirq_lines[] = {
+ { 19, 0, S32G_IMCR_SIUL_EIRQ0, 0x2 }, /* PB_03 -> EIRQ0 */
+ { 20, 1, S32G_IMCR_SIUL_EIRQ1, 0x2 }, /* PB_04 -> EIRQ1 */
+ { 21, 2, S32G_IMCR_SIUL_EIRQ2, 0x2 }, /* PB_05 -> EIRQ2 */
+ { 22, 3, S32G_IMCR_SIUL_EIRQ3, 0x2 }, /* PB_06 -> EIRQ3 */
+ { 23, 4, S32G_IMCR_SIUL_EIRQ4, 0x2 }, /* PB_07 -> EIRQ4 */
+ { 24, 5, S32G_IMCR_SIUL_EIRQ5, 0x2 }, /* PB_08 -> EIRQ5 */
+ { 25, 6, S32G_IMCR_SIUL_EIRQ6, 0x2 }, /* PB_09 -> EIRQ6 */
+ { 26, 7, S32G_IMCR_SIUL_EIRQ7, 0x2 }, /* PB_10 -> EIRQ7 */
+ { 27, 8, S32G_IMCR_SIUL_EIRQ8, 0x2 }, /* PB_11 -> EIRQ8 */
+ { 28, 9, S32G_IMCR_SIUL_EIRQ9, 0x2 }, /* PB_12 -> EIRQ9 */
+ { 29, 10, S32G_IMCR_SIUL_EIRQ10, 0x2 }, /* PB_13 -> EIRQ10 */
+ { 31, 12, S32G_IMCR_SIUL_EIRQ12, 0x2 }, /* PB_15 -> EIRQ12 */
+ { 33, 13, S32G_IMCR_SIUL_EIRQ13, 0x2 }, /* PC_01 -> EIRQ13 */
+ { 34, 14, S32G_IMCR_SIUL_EIRQ14, 0x2 }, /* PC_02 -> EIRQ14 */
+ { 35, 15, S32G_IMCR_SIUL_EIRQ15, 0x2 }, /* PC_03 -> EIRQ15 */
+ { 36, 27, S32G_IMCR_SIUL_EIRQ27, 0x2 }, /* PC_04 -> EIRQ27 */
+ { 37, 28, S32G_IMCR_SIUL_EIRQ28, 0x2 }, /* PC_05 -> EIRQ28 */
+ { 38, 29, S32G_IMCR_SIUL_EIRQ29, 0x2 }, /* PC_06 -> EIRQ29 */
+ { 39, 30, S32G_IMCR_SIUL_EIRQ30, 0x2 }, /* PC_07 -> EIRQ30 */
+ { 40, 31, S32G_IMCR_SIUL_EIRQ31, 0x2 }, /* PC_08 -> EIRQ31 */
+ { 113, 23, S32G_IMCR_SIUL_EIRQ23, 0x2 }, /* PH_01 -> EIRQ23 */
+ { 114, 24, S32G_IMCR_SIUL_EIRQ24, 0x2 }, /* PH_02 -> EIRQ24 */
+ { 115, 25, S32G_IMCR_SIUL_EIRQ25, 0x2 }, /* PH_03 -> EIRQ25 */
+ { 117, 26, S32G_IMCR_SIUL_EIRQ26, 0x2 }, /* PH_05 -> EIRQ26 */
+ { 151, 0, S32G_IMCR_SIUL_EIRQ0, 0x3 }, /* PJ_07 -> EIRQ0 */
+ { 152, 1, S32G_IMCR_SIUL_EIRQ1, 0x3 }, /* PJ_08 -> EIRQ1 */
+ { 154, 8, S32G_IMCR_SIUL_EIRQ8, 0x3 }, /* PJ_10 -> EIRQ8 */
+ { 160, 9, S32G_IMCR_SIUL_EIRQ9, 0x3 }, /* PK_00 -> EIRQ9 */
+ { 165, 10, S32G_IMCR_SIUL_EIRQ10, 0x3 }, /* PK_05 -> EIRQ10 */
+ { 168, 11, S32G_IMCR_SIUL_EIRQ11, 0x2 }, /* PK_08 -> EIRQ11 */
+ { 177, 2, S32G_IMCR_SIUL_EIRQ2, 0x3 }, /* PL_01 -> EIRQ2 */
+ { 178, 3, S32G_IMCR_SIUL_EIRQ3, 0x3 }, /* PL_02 -> EIRQ3 */
+ { 179, 4, S32G_IMCR_SIUL_EIRQ4, 0x3 }, /* PL_03 -> EIRQ4 */
+ { 180, 5, S32G_IMCR_SIUL_EIRQ5, 0x3 }, /* PL_04 -> EIRQ5 */
+ { 181, 6, S32G_IMCR_SIUL_EIRQ6, 0x3 }, /* PL_05 -> EIRQ6 */
+ { 182, 7, S32G_IMCR_SIUL_EIRQ7, 0x3 }, /* PL_06 -> EIRQ7 */
+ { 184, 16, S32G_IMCR_SIUL_EIRQ16, 0x2 }, /* PL_08 -> EIRQ16 */
+ { 185, 17, S32G_IMCR_SIUL_EIRQ17, 0x2 }, /* PL_09 -> EIRQ17 */
+ { 186, 18, S32G_IMCR_SIUL_EIRQ18, 0x2 }, /* PL_10 -> EIRQ18 */
+ { 187, 19, S32G_IMCR_SIUL_EIRQ19, 0x2 }, /* PL_11 -> EIRQ19 */
+ { 188, 20, S32G_IMCR_SIUL_EIRQ20, 0x2 }, /* PL_12 -> EIRQ20 */
+ { 189, 21, S32G_IMCR_SIUL_EIRQ21, 0x2 }, /* PL_13 -> EIRQ21 */
+ { 190, 22, S32G_IMCR_SIUL_EIRQ22, 0x2 }, /* PL_14 -> EIRQ22 */
+};
+
+static const struct s32_eirq_data s32g_eirq_data = {
+ .num_eirqs = 32,
+ .num_lines = ARRAY_SIZE(s32g_eirq_lines),
+ .lines = s32g_eirq_lines,
+};
+
/* Legacy data for old DT bindings without GPIO support */
static const struct s32_pinctrl_soc_data legacy_s32g_pinctrl_data = {
.pins = s32_pinctrl_pads_siul2,
@@ -810,6 +881,7 @@ static const struct s32_pinctrl_soc_data s32g_pinctrl_data = {
.num_gpio_ranges = ARRAY_SIZE(s32_gpio_ranges_siul2),
.gpio_pad_maps = s32g_gpio_pad_maps,
.num_gpio_pad_maps = ARRAY_SIZE(s32g_gpio_pad_maps),
+ .eirq_data = &s32g_eirq_data,
};

static const struct of_device_id s32_pinctrl_of_match[] = {
--
2.34.1