1 /*
2 * Copyright 2022-2023 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_s32_siul2_eirq
8
9 #include <soc.h>
10 #include <zephyr/irq.h>
11 #include <zephyr/sys/sys_io.h>
12 #include <zephyr/drivers/pinctrl.h>
13 #include <zephyr/drivers/interrupt_controller/intc_eirq_nxp_s32.h>
14
15 #include <Siul2_Icu_Ip_Irq.h>
16
17 #define NXP_S32_NUM_CHANNELS SIUL2_ICU_IP_NUM_OF_CHANNELS
18 /*
19 * The macros from low level driver contains a bracket so
20 * it cannot be used for some Zephyr macros (e.g LISTIFY).
21 * This just does remove the bracket to be used for such macro.
22 */
23 #define NXP_S32_NUM_CHANNELS_DEBRACKET __DEBRACKET SIUL2_ICU_IP_NUM_OF_CHANNELS
24
25 struct eirq_nxp_s32_config {
26 uint8_t instance;
27 mem_addr_t disr0;
28 mem_addr_t direr0;
29
30 const Siul2_Icu_Ip_ConfigType *icu_cfg;
31 const struct pinctrl_dev_config *pincfg;
32 };
33
34 /* Wrapper callback for each EIRQ line, from low level driver callback to GPIO callback */
35 struct eirq_nxp_s32_cb {
36 eirq_nxp_s32_callback_t cb;
37 uint8_t pin;
38 void *data;
39 };
40
41 struct eirq_nxp_s32_data {
42 struct eirq_nxp_s32_cb *cb;
43 };
44
eirq_nxp_s32_set_callback(const struct device * dev,uint8_t line,eirq_nxp_s32_callback_t cb,uint8_t pin,void * arg)45 int eirq_nxp_s32_set_callback(const struct device *dev, uint8_t line,
46 eirq_nxp_s32_callback_t cb, uint8_t pin, void *arg)
47 {
48 struct eirq_nxp_s32_data *data = dev->data;
49
50 __ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
51
52 if (data->cb[line].cb) {
53 return -EBUSY;
54 }
55
56 data->cb[line].cb = cb;
57 data->cb[line].pin = pin;
58 data->cb[line].data = arg;
59
60 return 0;
61 }
62
eirq_nxp_s32_unset_callback(const struct device * dev,uint8_t line)63 void eirq_nxp_s32_unset_callback(const struct device *dev, uint8_t line)
64 {
65 struct eirq_nxp_s32_data *data = dev->data;
66
67 __ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
68
69 data->cb[line].cb = NULL;
70 data->cb[line].pin = 0;
71 data->cb[line].data = NULL;
72 }
73
eirq_nxp_s32_enable_interrupt(const struct device * dev,uint8_t line,Siul2_Icu_Ip_EdgeType edge_type)74 void eirq_nxp_s32_enable_interrupt(const struct device *dev, uint8_t line,
75 Siul2_Icu_Ip_EdgeType edge_type)
76 {
77 const struct eirq_nxp_s32_config *config = dev->config;
78
79 __ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
80
81 Siul2_Icu_Ip_SetActivationCondition(config->instance, line, edge_type);
82 Siul2_Icu_Ip_EnableNotification(config->instance, line);
83 Siul2_Icu_Ip_EnableInterrupt(config->instance, line);
84 }
85
eirq_nxp_s32_disable_interrupt(const struct device * dev,uint8_t line)86 void eirq_nxp_s32_disable_interrupt(const struct device *dev, uint8_t line)
87 {
88 const struct eirq_nxp_s32_config *config = dev->config;
89
90 __ASSERT(line < NXP_S32_NUM_CHANNELS, "Interrupt line is out of range");
91
92 Siul2_Icu_Ip_DisableInterrupt(config->instance, line);
93 Siul2_Icu_Ip_DisableNotification(config->instance, line);
94 Siul2_Icu_Ip_SetActivationCondition(config->instance, line, SIUL2_ICU_DISABLE);
95 }
96
eirq_nxp_s32_get_pending(const struct device * dev)97 uint32_t eirq_nxp_s32_get_pending(const struct device *dev)
98 {
99 const struct eirq_nxp_s32_config *config = dev->config;
100
101 return sys_read32(config->disr0) & sys_read32(config->direr0);
102 }
103
eirq_nxp_s32_callback(const struct device * dev,uint8 line)104 static void eirq_nxp_s32_callback(const struct device *dev, uint8 line)
105 {
106 const struct eirq_nxp_s32_data *data = dev->data;
107
108 if (data->cb[line].cb != NULL) {
109 data->cb[line].cb(data->cb[line].pin, data->cb[line].data);
110 }
111 }
112
eirq_nxp_s32_init(const struct device * dev)113 static int eirq_nxp_s32_init(const struct device *dev)
114 {
115 const struct eirq_nxp_s32_config *config = dev->config;
116 int err;
117
118 err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
119 if (err) {
120 return err;
121 }
122
123 if (Siul2_Icu_Ip_Init(config->instance, config->icu_cfg)) {
124 return -EINVAL;
125 }
126
127 return 0;
128 }
129
130 #define EIRQ_NXP_S32_CALLBACK(line, n) \
131 void nxp_s32_icu_##n##_eirq_line_##line##_callback(void) \
132 { \
133 eirq_nxp_s32_callback(DEVICE_DT_INST_GET(n), line); \
134 }
135
136 #define EIRQ_NXP_S32_CHANNEL_CONFIG(idx, n) \
137 { \
138 .hwChannel = idx, \
139 .digFilterEn = DT_INST_PROP_OR(DT_CHILD(n, line_##idx), filter_enable, 0), \
140 .maxFilterCnt = DT_INST_PROP_OR(DT_CHILD(n, line_##idx), filter_counter, 0), \
141 .intSel = SIUL2_ICU_IRQ, \
142 .intEdgeSel = SIUL2_ICU_DISABLE, \
143 .callback = NULL, \
144 .Siul2ChannelNotification = nxp_s32_icu_##n##_eirq_line_##idx##_callback, \
145 .callbackParam = 0U \
146 }
147
148 #define EIRQ_NXP_S32_CHANNELS_CONFIG(n) \
149 static const Siul2_Icu_Ip_ChannelConfigType eirq_##n##_channel_nxp_s32_cfg[] = { \
150 LISTIFY(NXP_S32_NUM_CHANNELS_DEBRACKET, EIRQ_NXP_S32_CHANNEL_CONFIG, (,), n) \
151 }
152
153 #define EIRQ_NXP_S32_INSTANCE_CONFIG(n) \
154 static const Siul2_Icu_Ip_InstanceConfigType eirq_##n##_instance_nxp_s32_cfg = { \
155 .intFilterClk = DT_INST_PROP_OR(n, filter_prescaler, 0), \
156 .altIntFilterClk = 0U, \
157 }
158
159 #define EIRQ_NXP_S32_COMBINE_CONFIG(n) \
160 static const Siul2_Icu_Ip_ConfigType eirq_##n##_nxp_s32_cfg = { \
161 .numChannels = NXP_S32_NUM_CHANNELS, \
162 .pInstanceConfig = &eirq_##n##_instance_nxp_s32_cfg, \
163 .pChannelsConfig = &eirq_##n##_channel_nxp_s32_cfg, \
164 }
165
166 #define EIRQ_NXP_S32_CONFIG(n) \
167 LISTIFY(NXP_S32_NUM_CHANNELS_DEBRACKET, EIRQ_NXP_S32_CALLBACK, (), n) \
168 EIRQ_NXP_S32_CHANNELS_CONFIG(n); \
169 EIRQ_NXP_S32_INSTANCE_CONFIG(n); \
170 EIRQ_NXP_S32_COMBINE_CONFIG(n);
171
172 #define _EIRQ_NXP_S32_IRQ_NAME(name) DT_CAT3(SIUL2_EXT_IRQ_, name, _ISR)
173
174 #define EIRQ_NXP_S32_IRQ_NAME(idx, n) \
175 COND_CODE_1(DT_INST_NODE_HAS_PROP(n, interrupt_names), \
176 (_EIRQ_NXP_S32_IRQ_NAME(DT_INST_STRING_TOKEN_BY_IDX(n, interrupt_names, idx))), \
177 (DT_CAT3(SIUL2_, n, _ICU_EIRQ_SINGLE_INT_HANDLER)))
178
179 #define _EIRQ_NXP_S32_IRQ_CONFIG(idx, n) \
180 do { \
181 IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, idx, irq), \
182 DT_INST_IRQ_BY_IDX(n, idx, priority), \
183 EIRQ_NXP_S32_IRQ_NAME(idx, n), \
184 DEVICE_DT_INST_GET(n), \
185 COND_CODE_1(CONFIG_GIC, (DT_INST_IRQ_BY_IDX(n, idx, flags)), (0))); \
186 irq_enable(DT_INST_IRQ_BY_IDX(n, idx, irq)); \
187 } while (false);
188
189 #define EIRQ_NXP_S32_IRQ_CONFIG(n) \
190 LISTIFY(DT_NUM_IRQS(DT_DRV_INST(n)), _EIRQ_NXP_S32_IRQ_CONFIG, (), n)
191
192 #define EIRQ_NXP_S32_HW_INSTANCE_CHECK(i, n) \
193 (((DT_REG_ADDR(DT_INST_PARENT(n))) == IP_SIUL2_##i##_BASE) ? i : 0)
194
195 #define EIRQ_NXP_S32_HW_INSTANCE(n) \
196 LISTIFY(__DEBRACKET SIUL2_INSTANCE_COUNT, EIRQ_NXP_S32_HW_INSTANCE_CHECK, (|), n)
197
198 #define EIRQ_NXP_S32_INIT_DEVICE(n) \
199 EIRQ_NXP_S32_CONFIG(n) \
200 PINCTRL_DT_INST_DEFINE(n); \
201 static const struct eirq_nxp_s32_config eirq_nxp_s32_conf_##n = { \
202 .instance = EIRQ_NXP_S32_HW_INSTANCE(n), \
203 .disr0 = (mem_addr_t)DT_INST_REG_ADDR_BY_NAME(n, disr0), \
204 .direr0 = (mem_addr_t)DT_INST_REG_ADDR_BY_NAME(n, direr0), \
205 .icu_cfg = (Siul2_Icu_Ip_ConfigType *)&eirq_##n##_nxp_s32_cfg, \
206 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n) \
207 }; \
208 static struct eirq_nxp_s32_cb eirq_nxp_s32_cb_##n[NXP_S32_NUM_CHANNELS]; \
209 static struct eirq_nxp_s32_data eirq_nxp_s32_data_##n = { \
210 .cb = eirq_nxp_s32_cb_##n, \
211 }; \
212 static int eirq_nxp_s32_init##n(const struct device *dev); \
213 DEVICE_DT_INST_DEFINE(n, \
214 eirq_nxp_s32_init##n, \
215 NULL, \
216 &eirq_nxp_s32_data_##n, \
217 &eirq_nxp_s32_conf_##n, \
218 PRE_KERNEL_2, \
219 CONFIG_INTC_INIT_PRIORITY, \
220 NULL); \
221 static int eirq_nxp_s32_init##n(const struct device *dev) \
222 { \
223 int err; \
224 \
225 err = eirq_nxp_s32_init(dev); \
226 if (err) { \
227 return err; \
228 } \
229 \
230 EIRQ_NXP_S32_IRQ_CONFIG(n); \
231 \
232 return 0; \
233 }
234
235 DT_INST_FOREACH_STATUS_OKAY(EIRQ_NXP_S32_INIT_DEVICE)
236