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

Khristine Andreea Barbulescu khristineandreea.barbulescu at oss.nxp.com
Thu Oct 8 00:14:35 PDT 2026


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 at oss.nxp.com>
---
 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




More information about the linux-arm-kernel mailing list