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