drivers: interrupt_controller: initial support for NXP S32Z27 EIRQ

Add initial support for the NXP S32Z27 SIUL2 External
Interrupt Controller. Each SIUL2 node has a child node
will act as an interrupt-controller that processes external
interrupt signals.

This driver is required to manage GPIO interrupts.

Signed-off-by: Dat Nguyen Duy <dat.nguyenduy@nxp.com>
diff --git a/boards/arm/s32z270dc2_r52/doc/index.rst b/boards/arm/s32z270dc2_r52/doc/index.rst
index 768b759..f6617db 100644
--- a/boards/arm/s32z270dc2_r52/doc/index.rst
+++ b/boards/arm/s32z270dc2_r52/doc/index.rst
@@ -36,6 +36,8 @@
 | SIUL2     | on-chip    | pinctrl                             |
 |           |            |                                     |
 |           |            | gpio                                |
+|           |            |                                     |
+|           |            | external interrupt controller       |
 +-----------+------------+-------------------------------------+
 | LINFlexD  | on-chip    | serial                              |
 +-----------+------------+-------------------------------------+
diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt
index 7eebf9a..5c09490 100644
--- a/drivers/interrupt_controller/CMakeLists.txt
+++ b/drivers/interrupt_controller/CMakeLists.txt
@@ -30,6 +30,7 @@
 zephyr_library_sources_ifdef(CONFIG_SWERV_PIC               intc_swerv_pic.c)
 zephyr_library_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ      intc_vexriscv_litex.c)
 zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC            intc_nuclei_eclic.c)
+zephyr_library_sources_ifdef(CONFIG_NXP_S32_EIRQ            intc_eirq_nxp_s32.c)
 
 if(CONFIG_INTEL_VTD_ICTL)
   zephyr_library_include_directories(${ZEPHYR_BASE}/arch/x86/include)
diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig
index 93d6332..fd2bd20 100644
--- a/drivers/interrupt_controller/Kconfig
+++ b/drivers/interrupt_controller/Kconfig
@@ -81,4 +81,6 @@
 
 source "drivers/interrupt_controller/Kconfig.plic"
 
+source "drivers/interrupt_controller/Kconfig.nxp_s32"
+
 endmenu
diff --git a/drivers/interrupt_controller/Kconfig.nxp_s32 b/drivers/interrupt_controller/Kconfig.nxp_s32
new file mode 100644
index 0000000..eefa70c
--- /dev/null
+++ b/drivers/interrupt_controller/Kconfig.nxp_s32
@@ -0,0 +1,11 @@
+# Configuration for NXP S32 external interrupt controller
+
+# Copyright 2022 NXP
+# SPDX-License-Identifier: Apache-2.0
+
+config NXP_S32_EIRQ
+	bool "External interrupt controller driver for NXP S32 MCUs"
+	default y
+	depends on DT_HAS_NXP_S32_SIUL2_EIRQ_ENABLED
+	help
+	  External interrupt controller driver for NXP S32 MCUs
diff --git a/drivers/interrupt_controller/intc_eirq_nxp_s32.c b/drivers/interrupt_controller/intc_eirq_nxp_s32.c
new file mode 100644
index 0000000..5ffb2b8
--- /dev/null
+++ b/drivers/interrupt_controller/intc_eirq_nxp_s32.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2022 NXP
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/irq.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/interrupt_controller/intc_eirq_nxp_s32.h>
+
+#include <Siul2_Icu_Ip_Irq.h>
+
+#define NXP_S32_NUM_CHANNELS		SIUL2_ICU_IP_NUM_OF_CHANNELS
+/*
+ * The macros from low level driver contains a bracket so
+ * it cannot be used for some Zephyr macros (e.g LISTIFY).
+ * This just does remove the bracket to be used for such macro.
+ */
+#define NXP_S32_NUM_CHANNELS_DEBRACKET	__DEBRACKET SIUL2_ICU_IP_NUM_OF_CHANNELS
+
+struct eirq_nxp_s32_config {
+	uint8_t instance;
+	mem_addr_t disr0;
+	mem_addr_t direr0;
+
+	const Siul2_Icu_Ip_ConfigType *icu_cfg;
+	const struct pinctrl_dev_config *pincfg;
+};
+
+/* Wrapper callback for each EIRQ line, from low level driver callback to GPIO callback */
+struct eirq_nxp_s32_cb {
+	eirq_nxp_s32_callback_t cb;
+	uint8_t pin;
+	void *data;
+};
+
+struct eirq_nxp_s32_data {
+	struct eirq_nxp_s32_cb *cb;
+};
+
+int eirq_nxp_s32_set_callback(const struct device *dev, uint8_t line,
+				eirq_nxp_s32_callback_t cb, uint8_t pin, void *arg)
+{
+	struct eirq_nxp_s32_data *data = dev->data;
+
+	__ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
+
+	if (data->cb[line].cb) {
+		return -EBUSY;
+	}
+
+	data->cb[line].cb   = cb;
+	data->cb[line].pin  = pin;
+	data->cb[line].data = arg;
+
+	return 0;
+}
+
+void eirq_nxp_s32_unset_callback(const struct device *dev, uint8_t line)
+{
+	struct eirq_nxp_s32_data *data = dev->data;
+
+	__ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
+
+	data->cb[line].cb    = NULL;
+	data->cb[line].pin   = 0;
+	data->cb[line].data  = NULL;
+}
+
+void eirq_nxp_s32_enable_interrupt(const struct device *dev, uint8_t line,
+					Siul2_Icu_Ip_EdgeType edge_type)
+{
+	const struct eirq_nxp_s32_config *config = dev->config;
+
+	__ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
+
+	Siul2_Icu_Ip_SetActivationCondition(config->instance, line, edge_type);
+	Siul2_Icu_Ip_EnableNotification(config->instance, line);
+	Siul2_Icu_Ip_EnableInterrupt(config->instance, line);
+}
+
+void eirq_nxp_s32_disable_interrupt(const struct device *dev, uint8_t line)
+{
+	const struct eirq_nxp_s32_config *config = dev->config;
+
+	__ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
+
+	Siul2_Icu_Ip_DisableInterrupt(config->instance, line);
+	Siul2_Icu_Ip_DisableNotification(config->instance, line);
+	Siul2_Icu_Ip_SetActivationCondition(config->instance, line, SIUL2_ICU_DISABLE);
+}
+
+uint32_t eirq_nxp_s32_get_pending(const struct device *dev)
+{
+	const struct eirq_nxp_s32_config *config = dev->config;
+
+	return sys_read32(config->disr0) & sys_read32(config->direr0);
+}
+
+static void eirq_nxp_s32_callback(const struct device *dev, uint8 line)
+{
+	const struct eirq_nxp_s32_data *data = dev->data;
+
+	if (data->cb[line].cb != NULL) {
+		data->cb[line].cb(data->cb[line].pin, data->cb[line].data);
+	}
+}
+
+static int eirq_nxp_s32_init(const struct device *dev)
+{
+	const struct eirq_nxp_s32_config *config = dev->config;
+	int err;
+
+	err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
+	if (err) {
+		return err;
+	}
+
+	if (Siul2_Icu_Ip_Init(config->instance, config->icu_cfg)) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define EIRQ_NXP_S32_NODE(n)	DT_NODELABEL(eirq##n)
+
+#define EIRQ_NXP_S32_CALLBACK(line, n)								\
+	void nxp_s32_icu_##n##_eirq_line_##line##_callback(void)				\
+	{											\
+		const struct device *dev = DEVICE_DT_GET(EIRQ_NXP_S32_NODE(n));			\
+												\
+		eirq_nxp_s32_callback(dev, line);						\
+	}
+
+#define EIRQ_NXP_S32_CHANNEL_CONFIG(idx, n)							\
+	{											\
+		.hwChannel = idx,								\
+		.digFilterEn = DT_PROP_OR(DT_CHILD(EIRQ_NXP_S32_NODE(n), line_##idx),		\
+						filter_enable, 0),				\
+		.maxFilterCnt = DT_PROP_OR(DT_CHILD(EIRQ_NXP_S32_NODE(n), line_##idx),		\
+						filter_counter, 0),				\
+		.intSel = SIUL2_ICU_IRQ,							\
+		.intEdgeSel = SIUL2_ICU_DISABLE,						\
+		.callback = NULL,								\
+		.Siul2ChannelNotification = nxp_s32_icu_##n##_eirq_line_##idx##_callback,	\
+		.callbackParam = 0U								\
+	}
+
+#define EIRQ_NXP_S32_CHANNELS_CONFIG(n)								\
+	static const Siul2_Icu_Ip_ChannelConfigType eirq_##n##_channel_nxp_s32_cfg[] = {	\
+		LISTIFY(NXP_S32_NUM_CHANNELS_DEBRACKET,	EIRQ_NXP_S32_CHANNEL_CONFIG, (,), n)	\
+	}
+
+#define EIRQ_NXP_S32_INSTANCE_CONFIG(n)								\
+	static const Siul2_Icu_Ip_InstanceConfigType eirq_##n##_instance_nxp_s32_cfg = {	\
+		.intFilterClk = DT_PROP_OR(EIRQ_NXP_S32_NODE(n),				\
+						filter_prescaler, (0)),				\
+		.altIntFilterClk = 0U,								\
+	}
+
+#define EIRQ_NXP_S32_COMBINE_CONFIG(n)								\
+	static const Siul2_Icu_Ip_ConfigType eirq_##n##_nxp_s32_cfg = {				\
+		.numChannels	 = NXP_S32_NUM_CHANNELS,					\
+		.pInstanceConfig = &eirq_##n##_instance_nxp_s32_cfg,				\
+		.pChannelsConfig = &eirq_##n##_channel_nxp_s32_cfg,				\
+	}
+
+#define EIRQ_NXP_S32_CONFIG(n)									\
+	LISTIFY(NXP_S32_NUM_CHANNELS_DEBRACKET, EIRQ_NXP_S32_CALLBACK, (), n)			\
+	EIRQ_NXP_S32_CHANNELS_CONFIG(n);							\
+	EIRQ_NXP_S32_INSTANCE_CONFIG(n);							\
+	EIRQ_NXP_S32_COMBINE_CONFIG(n);
+
+#define EIRQ_NXP_S32_INIT_DEVICE(n)								\
+	EIRQ_NXP_S32_CONFIG(n)									\
+	PINCTRL_DT_DEFINE(EIRQ_NXP_S32_NODE(n));						\
+	static const struct eirq_nxp_s32_config eirq_nxp_s32_conf_##n = {			\
+		.instance = n,									\
+		.disr0    = (mem_addr_t)DT_REG_ADDR_BY_NAME(EIRQ_NXP_S32_NODE(n), disr0),	\
+		.direr0   = (mem_addr_t)DT_REG_ADDR_BY_NAME(EIRQ_NXP_S32_NODE(n), direr0),	\
+		.icu_cfg  = (Siul2_Icu_Ip_ConfigType *)&eirq_##n##_nxp_s32_cfg,			\
+		.pincfg   = PINCTRL_DT_DEV_CONFIG_GET(EIRQ_NXP_S32_NODE(n))			\
+	};											\
+	static struct eirq_nxp_s32_cb eirq_nxp_s32_cb_##n[NXP_S32_NUM_CHANNELS];		\
+	static struct eirq_nxp_s32_data eirq_nxp_s32_data_##n = {				\
+		.cb = eirq_nxp_s32_cb_##n,							\
+	};											\
+	static int eirq_nxp_s32_init##n(const struct device *dev);				\
+	DEVICE_DT_DEFINE(EIRQ_NXP_S32_NODE(n),							\
+		eirq_nxp_s32_init##n,								\
+		NULL,										\
+		&eirq_nxp_s32_data_##n,								\
+		&eirq_nxp_s32_conf_##n,								\
+		PRE_KERNEL_1,									\
+		CONFIG_INTC_INIT_PRIORITY,							\
+		NULL);										\
+	static int eirq_nxp_s32_init##n(const struct device *dev)				\
+	{											\
+		int err;									\
+												\
+		err = eirq_nxp_s32_init(dev);							\
+		if (err) {									\
+			return err;								\
+		}										\
+												\
+		IRQ_CONNECT(DT_IRQN(EIRQ_NXP_S32_NODE(n)),					\
+			DT_IRQ(EIRQ_NXP_S32_NODE(n), priority),					\
+			SIUL2_##n##_ICU_EIRQ_SINGLE_INT_HANDLER,				\
+			DEVICE_DT_GET(EIRQ_NXP_S32_NODE(n)),					\
+			DT_IRQ(EIRQ_NXP_S32_NODE(n), flags));					\
+												\
+		irq_enable(DT_IRQN(EIRQ_NXP_S32_NODE(n)));					\
+												\
+		return 0;									\
+	}
+
+#if DT_NODE_HAS_STATUS(EIRQ_NXP_S32_NODE(0), okay)
+EIRQ_NXP_S32_INIT_DEVICE(0)
+#endif
+
+#if DT_NODE_HAS_STATUS(EIRQ_NXP_S32_NODE(1), okay)
+EIRQ_NXP_S32_INIT_DEVICE(1)
+#endif
+
+#if DT_NODE_HAS_STATUS(EIRQ_NXP_S32_NODE(4), okay)
+EIRQ_NXP_S32_INIT_DEVICE(4)
+#endif
+
+#if DT_NODE_HAS_STATUS(EIRQ_NXP_S32_NODE(5), okay)
+EIRQ_NXP_S32_INIT_DEVICE(5)
+#endif
diff --git a/dts/arm/nxp/nxp_s32z27x_r52.dtsi b/dts/arm/nxp/nxp_s32z27x_r52.dtsi
index d46657e..d5236ce 100644
--- a/dts/arm/nxp/nxp_s32z27x_r52.dtsi
+++ b/dts/arm/nxp/nxp_s32z27x_r52.dtsi
@@ -195,6 +195,16 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			eirq0: eirq0@40520010 {
+				compatible = "nxp,s32-siul2-eirq";
+				reg = <0x40520010 0x04>, <0x40520018 0x04>;
+				reg-names = "disr0", "direr0";
+				interrupts = <GIC_SPI 514 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				status = "disabled";
+			};
+
 			gpioa: gpio@40521702 {
 				compatible = "nxp,s32-gpio";
 				reg = <0x40521702 0x02>, <0x40520240 0x40>;
@@ -232,6 +242,16 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			eirq1: eirq1@40d20010 {
+				compatible = "nxp,s32-siul2-eirq";
+				reg = <0x40d20010 0x04>, <0x40d20018 0x04>;
+				reg-names = "disr0", "direr0";
+				interrupts = <GIC_SPI 515 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				status = "disabled";
+			};
+
 			gpioc: gpio@40d21700 {
 				compatible = "nxp,s32-gpio";
 				reg = <0x40d21700 0x02>, <0x40d20280 0x40>;
@@ -293,6 +313,16 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			eirq4: eirq4@42520010 {
+				compatible = "nxp,s32-siul2-eirq";
+				reg = <0x42520010 0x04>, <0x42520018 0x04>;
+				reg-names = "disr0", "direr0";
+				interrupts = <GIC_SPI 516 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				status = "disabled";
+			};
+
 			gpioh: gpio@42521708 {
 				compatible = "nxp,s32-gpio";
 				reg = <0x42521708 0x02>, <0x42520380 0x40>;
@@ -351,6 +381,16 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			eirq5: eirq5@42d20010 {
+				compatible = "nxp,s32-siul2-eirq";
+				reg = <0x42d20010 0x04>, <0x42d20018 0x04>;
+				reg-names = "disr0", "direr0";
+				interrupts = <GIC_SPI 517 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+				status = "disabled";
+			};
+
 			gpiom: gpio@42d21710 {
 				compatible = "nxp,s32-gpio";
 				reg = <0x42d21710 0x02>, <0x42d20480 0x40>;
diff --git a/dts/bindings/interrupt-controller/nxp,s32-siul2-eirq.yaml b/dts/bindings/interrupt-controller/nxp,s32-siul2-eirq.yaml
new file mode 100644
index 0000000..322f0f9
--- /dev/null
+++ b/dts/bindings/interrupt-controller/nxp,s32-siul2-eirq.yaml
@@ -0,0 +1,62 @@
+# Copyright 2022 NXP
+#
+# SPDX-License-Identifier: Apache-2.0
+
+description: NXP S32 SIUL2 External Interrupts Request controller
+
+compatible: "nxp,s32-siul2-eirq"
+
+include: [interrupt-controller.yaml, pinctrl-device.yaml, base.yaml]
+
+properties:
+  reg:
+    required: true
+
+  reg-names:
+    required: true
+
+  pinctrl-0:
+    required: true
+
+  pinctrl-names:
+    required: true
+
+  filter-prescaler:
+    type: int
+    required: false
+    description: |
+      Setting the prescaler which selects the clock for all digital filters.
+      Valid range: 0 - 15.
+
+child-binding:
+  description: |
+    NXP S32 SIUL2 External Interrupt line configuration. For each
+    interrupt line that has specific requirements about digital
+    glitch filter, a node using this binding must be added, the
+    name must be "line_<line_number>". For example:
+
+      line_0: line_0 {
+          filter-enable;
+          filter-counter = <5>;
+      };
+
+    If the controller has no child node, the digital filter will be
+    disabled for all external interrupt lines.
+
+  properties:
+    filter-enable:
+      type: boolean
+      required: true
+      description: |
+        Enable digital glitch filter to filter out glitches on the input pad.
+
+    filter-counter:
+      type: int
+      required: true
+      description: |
+        Configuring the filter counter associated with digital glitch filter.
+        Valid range: 0 - 15.
+
+interrupt-cells:
+  - gpio-pin
+  - eirq-line
diff --git a/include/zephyr/drivers/interrupt_controller/intc_eirq_nxp_s32.h b/include/zephyr/drivers/interrupt_controller/intc_eirq_nxp_s32.h
new file mode 100644
index 0000000..7f96be3
--- /dev/null
+++ b/include/zephyr/drivers/interrupt_controller/intc_eirq_nxp_s32.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 NXP
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @brief Driver for External interrupt/event controller in NXP S32 MCUs
+ *
+ */
+
+#ifndef ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EIRQ_NXP_S32_H_
+#define ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EIRQ_NXP_S32_H_
+
+#include <Siul2_Icu_Ip.h>
+
+/* Wrapper callback for EIRQ line */
+typedef void (*eirq_nxp_s32_callback_t)(uint8_t pin, void *arg);
+
+/**
+ * @brief Unset EIRQ callback for line
+ *
+ * @param dev EIRQ device
+ * @param line EIRQ line
+ */
+void eirq_nxp_s32_unset_callback(const struct device *dev, uint8_t line);
+
+/**
+ * @brief Set EIRQ callback for line
+ *
+ * @param dev  EIRQ device
+ * @param line EIRQ line
+ * @param cb   Callback
+ * @param pin  GPIO pin
+ * @param arg  Callback data
+ *
+ * @retval 0 on SUCCESS
+ * @retval -EBUSY if callback for the line is already set
+ */
+int eirq_nxp_s32_set_callback(const struct device *dev, uint8_t line,
+				eirq_nxp_s32_callback_t cb, uint8_t pin, void *arg);
+
+/**
+ * @brief Set edge event and enable interrupt for EIRQ line
+ *
+ * @param dev  EIRQ device
+ * @param line EIRQ line
+ * @param edge_type Type of edge event
+ */
+void eirq_nxp_s32_enable_interrupt(const struct device *dev, uint8_t line,
+					Siul2_Icu_Ip_EdgeType edge_type);
+
+/**
+ * @brief Disable interrupt for EIRQ line
+ *
+ * @param dev  EIRQ device
+ * @param line EIRQ line
+ */
+void eirq_nxp_s32_disable_interrupt(const struct device *dev, uint8_t line);
+
+/**
+ * @brief Get pending interrupt for EIRQ device
+ *
+ * @param dev EIRQ device
+ * @return A mask contains pending flags
+ */
+uint32_t eirq_nxp_s32_get_pending(const struct device *dev);
+
+#endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_EIRQ_NXP_S32_H_ */
diff --git a/west.yml b/west.yml
index c6f0d70..2db159b 100644
--- a/west.yml
+++ b/west.yml
@@ -88,7 +88,7 @@
       groups:
         - hal
     - name: hal_nxp
-      revision: 9bc1e797921b2a4b246ce7647dc426549985e76a
+      revision: 6d48547251d471ecdca9560f297296a1f33ad16c
       path: modules/hal/nxp
       groups:
         - hal