1 /*
2 * Copyright (c) 2016 Open-RnD Sp. z o.o.
3 * Copyright (c) 2017 RnDity Sp. z o.o.
4 * Copyright (c) 2019-23 Linaro Limited
5 * Copyright (C) 2025 Savoir-faire Linux, Inc.
6 * Copyright (c) 2025 Alexander Kozhinov <ak.alexander.kozhinov@gmail.com>
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 /**
12 * @brief STM32 External Interrupt/Event Controller (EXTI) Driver
13 */
14
15 #include <soc.h>
16 #include <zephyr/device.h>
17 #include <zephyr/sys/util.h>
18 #include <zephyr/logging/log.h>
19 #include <zephyr/drivers/interrupt_controller/intc_exti_stm32.h>
20 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
21
22 #include "stm32_hsem.h"
23 #include "intc_exti_stm32_priv.h"
24
25 LOG_MODULE_REGISTER(exti_stm32, CONFIG_INTC_LOG_LEVEL);
26
27 #define IS_VALID_EXTI_LINE_NUM(line_num) ((line_num) < STM32_EXTI_TOTAL_LINES_NUM)
28
29 /*
30 * The boilerplate for COND_CODE_x is needed because the values are not 0/1
31 */
32 #if STM32_EXTI_TOTAL_LINES_NUM > 32
33 #define HAS_LINES_32_63 1
34 #if STM32_EXTI_TOTAL_LINES_NUM > 64
35 #define HAS_LINES_64_95 1
36 #endif /* STM32_EXTI_TOTAL_LINES_NUM > 64 */
37 #endif /* STM32_EXTI_TOTAL_LINES_NUM > 32 */
38
39 #define EXTI_FN_HANDLER(_fn, line_num, line) \
40 if (line_num < 32U) { \
41 _fn(0_31, line); \
42 IF_ENABLED(HAS_LINES_32_63, ( \
43 } else if (line_num < 64U) { \
44 _fn(32_63, line); \
45 )) \
46 IF_ENABLED(HAS_LINES_64_95, ( \
47 } else if (line_num < 96U) { \
48 _fn(64_95, line); \
49 )) \
50 } else { \
51 LOG_ERR("Invalid line number %u", line_num); \
52 __ASSERT_NO_MSG(0); \
53 }
54
55 #define EXTI_FN_RET_HANDLER(_fn, ret, line_num, line) \
56 if (line_num < 32U) { \
57 *ret = _fn(0_31, line); \
58 IF_ENABLED(HAS_LINES_32_63, ( \
59 } else if (line_num < 64U) { \
60 *ret = _fn(32_63, line); \
61 )) \
62 IF_ENABLED(HAS_LINES_64_95, ( \
63 } else if (line_num < 96U) { \
64 *ret = _fn(64_95, line); \
65 )) \
66 } else { \
67 LOG_ERR("Invalid line number %u", line_num); \
68 __ASSERT_NO_MSG(0); \
69 }
70
71
stm32_exti_is_pending(uint32_t line_num)72 bool stm32_exti_is_pending(uint32_t line_num)
73 {
74 bool ret = false;
75 const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
76
77 if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
78 LOG_ERR("Invalid line number %u", line_num);
79 return false;
80 }
81
82 z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
83
84 /*
85 * Note: we can't use EXTI_FN_HANDLER here because we care
86 * about the return value of EXTI_IS_ACTIVE_FLAG.
87 */
88 EXTI_FN_RET_HANDLER(EXTI_IS_ACTIVE_FLAG, &ret, line_num, line);
89
90 z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
91
92 return ret;
93 }
94
stm32_exti_clear_pending(uint32_t line_num)95 int stm32_exti_clear_pending(uint32_t line_num)
96 {
97 const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
98
99 if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
100 LOG_ERR("Invalid line number %u", line_num);
101 return -EINVAL;
102 }
103
104 z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
105
106 EXTI_FN_HANDLER(EXTI_CLEAR_FLAG, line_num, line);
107
108 z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
109
110 return 0;
111 }
112
stm32_exti_sw_interrupt(uint32_t line_num)113 int stm32_exti_sw_interrupt(uint32_t line_num)
114 {
115 const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
116
117 if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
118 LOG_ERR("Invalid line number %u", line_num);
119 return -EINVAL;
120 }
121
122 z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
123
124 EXTI_FN_HANDLER(EXTI_GENERATE_SWI, line_num, line);
125
126 z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
127
128 return 0;
129 }
130
131 /** Enables the peripheral clock required to access EXTI registers */
stm32_exti_enable_clocks(void)132 static int stm32_exti_enable_clocks(void)
133 {
134 /* Initialize to 0 for series where there is nothing to do. */
135 int ret = 0;
136
137 #if DT_NODE_HAS_PROP(EXTI_NODE, clocks)
138 const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
139
140 if (!device_is_ready(clk)) {
141 LOG_ERR("Clock control device not ready");
142 return -ENODEV;
143 }
144
145 const struct stm32_pclken pclken = {
146 .bus = DT_CLOCKS_CELL(EXTI_NODE, bus),
147 .enr = DT_CLOCKS_CELL(EXTI_NODE, bits)
148 };
149
150 ret = clock_control_on(clk, (clock_control_subsys_t) &pclken);
151 #endif
152 return ret;
153 }
154
155 /**
156 * @brief Initializes the EXTI interrupt controller driver
157 */
stm32_exti_init(const struct device * dev)158 static int stm32_exti_init(const struct device *dev)
159 {
160 ARG_UNUSED(dev);
161
162 return stm32_exti_enable_clocks();
163 }
164
165 /**
166 * @brief Enable EXTI interrupts.
167 *
168 * @param line_num EXTI line number
169 * @param line LL EXTI line
170 */
stm32_exti_enable_it(uint32_t line_num,uint32_t line)171 static void stm32_exti_enable_it(uint32_t line_num, uint32_t line)
172 {
173 EXTI_FN_HANDLER(EXTI_ENABLE_IT, line_num, line);
174 }
175
176 /**
177 * @brief Disable EXTI interrupts.
178 *
179 * @param line_num EXTI line number
180 * @param line LL EXTI line
181 */
stm32_exti_disable_it(uint32_t line_num,uint32_t line)182 static void stm32_exti_disable_it(uint32_t line_num, uint32_t line)
183 {
184 EXTI_FN_HANDLER(EXTI_DISABLE_IT, line_num, line);
185 }
186
187 /**
188 * @brief Enables rising trigger for specified EXTI line
189 *
190 * @param line_num EXTI line number
191 * @param line LL EXTI line
192 */
stm32_exti_enable_rising_trig(uint32_t line_num,uint32_t line)193 static void stm32_exti_enable_rising_trig(uint32_t line_num, uint32_t line)
194 {
195 EXTI_FN_HANDLER(EXTI_ENABLE_RISING_TRIG, line_num, line);
196 }
197
198 /**
199 * @brief Disables rising trigger for specified EXTI line
200 *
201 * @param line_num EXTI line number
202 * @param line LL EXTI line
203 */
stm32_exti_disable_rising_trig(uint32_t line_num,uint32_t line)204 static void stm32_exti_disable_rising_trig(uint32_t line_num, uint32_t line)
205 {
206 EXTI_FN_HANDLER(EXTI_DISABLE_RISING_TRIG, line_num, line);
207 }
208
209 /**
210 * @brief Enables falling trigger for specified EXTI line
211 *
212 * @param line_num EXTI line number
213 * @param line LL EXTI line
214 */
stm32_exti_enable_falling_trig(uint32_t line_num,uint32_t line)215 static void stm32_exti_enable_falling_trig(uint32_t line_num, uint32_t line)
216 {
217 EXTI_FN_HANDLER(EXTI_ENABLE_FALLING_TRIG, line_num, line);
218 }
219
220 /**
221 * @brief Disables falling trigger for specified EXTI line
222 *
223 * @param line_num EXTI line number
224 * @param line LL EXTI line
225 */
stm32_exti_disable_falling_trig(uint32_t line_num,uint32_t line)226 static void stm32_exti_disable_falling_trig(uint32_t line_num, uint32_t line)
227 {
228 EXTI_FN_HANDLER(EXTI_DISABLE_FALLING_TRIG, line_num, line);
229 }
230
231 /**
232 * @brief Selects EXTI trigger mode
233 *
234 * @param line_num EXTI line number
235 * @param line LL EXTI line
236 * @param mode EXTI mode
237 */
stm32_exti_select_line_trigger(uint32_t line_num,uint32_t line,uint32_t trg)238 static void stm32_exti_select_line_trigger(uint32_t line_num, uint32_t line,
239 uint32_t trg)
240 {
241 switch (trg) {
242 case STM32_EXTI_TRIG_NONE:
243 stm32_exti_disable_rising_trig(line_num, line);
244 stm32_exti_disable_falling_trig(line_num, line);
245 break;
246 case STM32_EXTI_TRIG_RISING:
247 stm32_exti_enable_rising_trig(line_num, line);
248 stm32_exti_disable_falling_trig(line_num, line);
249 break;
250 case STM32_EXTI_TRIG_FALLING:
251 stm32_exti_enable_falling_trig(line_num, line);
252 stm32_exti_disable_rising_trig(line_num, line);
253 break;
254 case STM32_EXTI_TRIG_BOTH:
255 stm32_exti_enable_rising_trig(line_num, line);
256 stm32_exti_enable_falling_trig(line_num, line);
257 break;
258 default:
259 LOG_ERR("Unsupported EXTI trigger 0x%X", trg);
260 break;
261 }
262 }
263
264 /**
265 * @brief Enable EXTI event.
266 *
267 * @param line_num EXTI line number
268 * @param line LL EXTI line
269 */
stm32_exti_enable_event(uint32_t line_num,uint32_t line)270 static void stm32_exti_enable_event(uint32_t line_num, uint32_t line)
271 {
272 EXTI_FN_HANDLER(EXTI_ENABLE_EVENT, line_num, line);
273 }
274
275 /**
276 * @brief Disable EXTI interrupts.
277 *
278 * @param line_num EXTI line number
279 * @param line LL EXTI line
280 */
stm32_exti_disable_event(uint32_t line_num,uint32_t line)281 static void stm32_exti_disable_event(uint32_t line_num, uint32_t line)
282 {
283 EXTI_FN_HANDLER(EXTI_DISABLE_EVENT, line_num, line);
284 }
285
286 /**
287 * @brief Enables external interrupt/event for specified EXTI line
288 *
289 * @param line_num EXTI line number
290 * @param line LL EXTI line
291 * @param mode EXTI mode
292 */
stm32_exti_set_mode(uint32_t line_num,uint32_t line,stm32_exti_mode mode)293 static void stm32_exti_set_mode(uint32_t line_num, uint32_t line,
294 stm32_exti_mode mode)
295 {
296 switch (mode) {
297 case STM32_EXTI_MODE_NONE:
298 stm32_exti_disable_event(line_num, line);
299 stm32_exti_disable_it(line_num, line);
300 break;
301 case STM32_EXTI_MODE_IT:
302 stm32_exti_disable_event(line_num, line);
303 stm32_exti_enable_it(line_num, line);
304 break;
305 case STM32_EXTI_MODE_EVENT:
306 stm32_exti_disable_it(line_num, line);
307 stm32_exti_enable_event(line_num, line);
308 break;
309 case STM32_EXTI_MODE_BOTH:
310 stm32_exti_enable_it(line_num, line);
311 stm32_exti_enable_event(line_num, line);
312 break;
313 default:
314 LOG_ERR("Unsupported EXTI mode %u", mode);
315 break;
316 }
317 }
318
stm32_exti_enable(uint32_t line_num,stm32_exti_trigger_type trigger,stm32_exti_mode mode)319 int stm32_exti_enable(uint32_t line_num, stm32_exti_trigger_type trigger,
320 stm32_exti_mode mode)
321 {
322 const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
323
324 if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
325 LOG_ERR("Invalid line number %u", line_num);
326 return -EINVAL;
327 }
328
329 z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
330
331 stm32_exti_select_line_trigger(line_num, line, trigger);
332 stm32_exti_set_mode(line_num, line, mode);
333
334 z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
335
336 return 0;
337 }
338
stm32_exti_disable(uint32_t line_num)339 int stm32_exti_disable(uint32_t line_num)
340 {
341 const uint32_t line = exti_linenum_to_ll_exti_line(line_num);
342
343 if (!IS_VALID_EXTI_LINE_NUM(line_num)) {
344 LOG_ERR("Invalid line number %u", line_num);
345 return -EINVAL;
346 }
347
348 z_stm32_hsem_lock(CFG_HW_EXTI_SEMID, HSEM_LOCK_DEFAULT_RETRY);
349
350 stm32_exti_set_mode(line_num, line, STM32_EXTI_MODE_NONE);
351 stm32_exti_select_line_trigger(line_num, line, STM32_EXTI_TRIG_NONE);
352
353 z_stm32_hsem_unlock(CFG_HW_EXTI_SEMID);
354
355 return 0;
356 }
357
358 DEVICE_DT_DEFINE(EXTI_NODE, &stm32_exti_init,
359 NULL, NULL, NULL,
360 PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY,
361 NULL);
362