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