1 /*
2 * Copyright (c) 2020 - 2024 Renesas Electronics Corporation and/or its affiliates
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 /***********************************************************************************************************************
8 * Includes
9 **********************************************************************************************************************/
10 #include "r_icu.h"
11 #include "r_icu_cfg.h"
12
13 /***********************************************************************************************************************
14 * Macro definitions
15 **********************************************************************************************************************/
16
17 /** "ICU" in ASCII, used to determine if channel is open. */
18 #define ICU_OPEN (0x00494355U)
19
20 #define ICU_SAFETY_REGISTER_OFFSET (14)
21 #define ICU_PORTNF_CLKSEL_MASK (0x03U)
22 #define ICU_PORTNF_FLTSEL_MASK (0x01U)
23 #define ICU_PORTNF_MD_MASK (0x03U)
24 #define ICU_NS_PORTNF_OFFSET(channel) (channel * 2)
25 #define ICU_S_PORTNF_OFFSET(channel) ((channel - ICU_SAFETY_REGISTER_OFFSET) * 2)
26
27 #define ICU_PORTNF_MD_LEVEL_LOW (0x00)
28 #define ICU_PORTNF_MD_FALLING_EDGE (0x01)
29 #define ICU_PORTNF_MD_RISING_EDGE (0x02)
30 #define ICU_PORTNF_MD_BOTH_EDGE (0x03)
31
32 /***********************************************************************************************************************
33 * Private function prototypes
34 **********************************************************************************************************************/
35 void r_icu_isr(void);
36
37 /***********************************************************************************************************************
38 * Private global variables
39 **********************************************************************************************************************/
40
41 /* Convert api defined value to PORT_NF_MD bit setting value. */
42 static const uint8_t g_icu_detect_mode[] =
43 {
44 [EXTERNAL_IRQ_TRIG_FALLING] = ICU_PORTNF_MD_FALLING_EDGE,
45 [EXTERNAL_IRQ_TRIG_RISING] = ICU_PORTNF_MD_RISING_EDGE,
46 [EXTERNAL_IRQ_TRIG_BOTH_EDGE] = ICU_PORTNF_MD_BOTH_EDGE,
47 [EXTERNAL_IRQ_TRIG_LEVEL_LOW] = ICU_PORTNF_MD_LEVEL_LOW,
48 };
49
50 /***********************************************************************************************************************
51 * Global Variables
52 **********************************************************************************************************************/
53
54 /* ICU implementation of External IRQ API. */
55 const external_irq_api_t g_external_irq_on_icu =
56 {
57 .open = R_ICU_ExternalIrqOpen,
58 .enable = R_ICU_ExternalIrqEnable,
59 .disable = R_ICU_ExternalIrqDisable,
60 .callbackSet = R_ICU_ExternalIrqCallbackSet,
61 .close = R_ICU_ExternalIrqClose,
62 };
63
64 /*******************************************************************************************************************//**
65 * @addtogroup ICU
66 * @{
67 **********************************************************************************************************************/
68
69 /***********************************************************************************************************************
70 * Functions
71 **********************************************************************************************************************/
72
73 /*******************************************************************************************************************//**
74 * Configure an IRQ input pin for use with the external interrupt interface. Implements @ref external_irq_api_t::open.
75 *
76 * The Open function is responsible for preparing an external IRQ pin for operation.
77 *
78 * @retval FSP_SUCCESS Open successful.
79 * @retval FSP_ERR_ASSERTION One of the following is invalid:
80 * - p_instance_ctrl or p_cfg is NULL
81 * @retval FSP_ERR_ALREADY_OPEN The channel specified has already been opened. No configurations were changed.
82 * Call the associated Close function to reconfigure the channel.
83 * @retval FSP_ERR_IP_CHANNEL_NOT_PRESENT The channel requested in p_cfg is not available on the device selected in
84 * r_bsp_cfg.h.
85 * @retval FSP_ERR_INVALID_ARGUMENT p_cfg->p_callback is not NULL, but ISR is not enabled. ISR must be enabled to
86 * use callback function.
87 *
88 * @note This function is reentrant for different channels. It is not reentrant for the same channel.
89 **********************************************************************************************************************/
R_ICU_ExternalIrqOpen(external_irq_ctrl_t * const p_ctrl,external_irq_cfg_t const * const p_cfg)90 fsp_err_t R_ICU_ExternalIrqOpen (external_irq_ctrl_t * const p_ctrl, external_irq_cfg_t const * const p_cfg)
91 {
92 icu_instance_ctrl_t * p_instance_ctrl = (icu_instance_ctrl_t *) p_ctrl;
93
94 #if ICU_CFG_PARAM_CHECKING_ENABLE
95 FSP_ASSERT(NULL != p_instance_ctrl);
96 FSP_ERROR_RETURN(ICU_OPEN != p_instance_ctrl->open, FSP_ERR_ALREADY_OPEN);
97 FSP_ASSERT(NULL != p_cfg);
98 FSP_ERROR_RETURN(0 != ((1U << p_cfg->channel) & BSP_FEATURE_ICU_IRQ_CHANNELS_MASK), FSP_ERR_IP_CHANNEL_NOT_PRESENT);
99
100 /* Callback must be used with a valid interrupt priority otherwise it will never be called. */
101 if (p_cfg->p_callback)
102 {
103 FSP_ERROR_RETURN(BSP_IRQ_DISABLED != p_cfg->ipl, FSP_ERR_INVALID_ARGUMENT);
104 }
105 #endif
106
107 p_instance_ctrl->irq = p_cfg->irq;
108 p_instance_ctrl->channel = p_cfg->channel;
109
110 /* Initialize control block. */
111 p_instance_ctrl->p_callback = p_cfg->p_callback;
112 p_instance_ctrl->p_context = p_cfg->p_context;
113 p_instance_ctrl->p_callback_memory = NULL;
114
115 if (p_instance_ctrl->channel < ICU_SAFETY_REGISTER_OFFSET)
116 {
117 /* Set the digital filter divider. */
118 uint32_t clksel = R_ICU_NS->NS_PORTNF_CLKSEL;
119 clksel &= ~(ICU_PORTNF_CLKSEL_MASK << ICU_NS_PORTNF_OFFSET(p_cfg->channel));
120 clksel |= (uint32_t) (p_cfg->clock_source_div << ICU_NS_PORTNF_OFFSET(p_cfg->channel));
121
122 /* Enable/Disable digital filter. */
123 uint32_t fltsel = R_ICU_NS->NS_PORTNF_FLTSEL;
124 fltsel &= ~(ICU_PORTNF_FLTSEL_MASK << p_cfg->channel);
125 fltsel |= (uint32_t) (((true == p_cfg->filter_enable) ? 1U : 0U) << p_cfg->channel);
126
127 /* Set the IRQ trigger. */
128 uint32_t md = R_ICU_NS->NS_PORTNF_MD;
129 md &= ~(ICU_PORTNF_MD_MASK << ICU_NS_PORTNF_OFFSET(p_cfg->channel));
130 md |= (uint32_t) (g_icu_detect_mode[p_cfg->trigger] << (ICU_NS_PORTNF_OFFSET(p_cfg->channel)));
131
132 /* Write NS_PORTNF_CLKSEL. */
133 R_ICU_NS->NS_PORTNF_CLKSEL = clksel;
134
135 /* Write NS_PORTNF_MD. */
136 R_ICU_NS->NS_PORTNF_MD = md;
137
138 /* Write NS_PORTNF_FLTSEL. */
139 R_ICU_NS->NS_PORTNF_FLTSEL = fltsel;
140 }
141 else
142 {
143 #if (1 == BSP_FEATURE_ICU_SAFETY_REGISTER_TYPE)
144
145 /* Set the digital filter divider. */
146 uint32_t clksel = R_ICU->S_PORTNF_CLKSEL;
147 clksel &= ~(ICU_PORTNF_CLKSEL_MASK << ICU_S_PORTNF_OFFSET(p_cfg->channel));
148 clksel |= (uint32_t) (p_cfg->clock_source_div << ICU_S_PORTNF_OFFSET(p_cfg->channel));
149
150 /* Enable/Disable digital filter. */
151 uint32_t fltsel = R_ICU->S_PORTNF_FLTSEL;
152 fltsel &= ~(ICU_PORTNF_FLTSEL_MASK << (p_cfg->channel - ICU_SAFETY_REGISTER_OFFSET));
153 fltsel |=
154 (uint32_t) (((true == p_cfg->filter_enable) ? 1U : 0U) << (p_cfg->channel - ICU_SAFETY_REGISTER_OFFSET));
155
156 /* Set the IRQ trigger. */
157 uint32_t md = R_ICU->S_PORTNF_MD;
158 md &= ~(ICU_PORTNF_MD_MASK << ICU_S_PORTNF_OFFSET(p_cfg->channel));
159 md |= (uint32_t) (g_icu_detect_mode[p_cfg->trigger] << (ICU_S_PORTNF_OFFSET(p_cfg->channel)));
160
161 /* Write S_PORTNF_CLKSEL. */
162 R_ICU->S_PORTNF_CLKSEL = clksel;
163
164 /* Write S_PORTNF_MD. */
165 R_ICU->S_PORTNF_MD = md;
166
167 /* Write S_PORTNF_FLTSEL. */
168 R_ICU->S_PORTNF_FLTSEL = fltsel;
169 #else
170
171 /* Set the digital filter divider. */
172 uint32_t clksel = R_ICU_S->S_PORTNF_CLKSEL;
173 clksel &= ~(ICU_PORTNF_CLKSEL_MASK << ICU_S_PORTNF_OFFSET(p_cfg->channel));
174 clksel |= (uint32_t) (p_cfg->clock_source_div << ICU_S_PORTNF_OFFSET(p_cfg->channel));
175
176 /* Enable/Disable digital filter. */
177 uint32_t fltsel = R_ICU_S->S_PORTNF_FLTSEL;
178 fltsel &= ~(ICU_PORTNF_FLTSEL_MASK << (p_cfg->channel - ICU_SAFETY_REGISTER_OFFSET));
179 fltsel |=
180 (uint32_t) (((true == p_cfg->filter_enable) ? 1U : 0U) << (p_cfg->channel - ICU_SAFETY_REGISTER_OFFSET));
181
182 /* Set the IRQ trigger. */
183 uint32_t md = R_ICU_S->S_PORTNF_MD;
184 md &= ~(ICU_PORTNF_MD_MASK << ICU_S_PORTNF_OFFSET(p_cfg->channel));
185 md |= (uint32_t) (g_icu_detect_mode[p_cfg->trigger] << (ICU_S_PORTNF_OFFSET(p_cfg->channel)));
186
187 /* Write S_PORTNF_CLKSEL. */
188 R_ICU_S->S_PORTNF_CLKSEL = clksel;
189
190 /* Write S_PORTNF_MD. */
191 R_ICU_S->S_PORTNF_MD = md;
192
193 /* Write S_PORTNF_FLTSEL. */
194 R_ICU_S->S_PORTNF_FLTSEL = fltsel;
195 #endif
196 }
197
198 /* NOTE: User can have the driver opened when the IRQ is not in the vector table. This is for use cases
199 * where the external IRQ driver is used to generate ELC events only (without CPU interrupts).
200 * In such cases we will not set the IRQ priority but will continue with the processing.
201 */
202 if (p_instance_ctrl->irq >= 0)
203 {
204 /* The detection type on the ICU peripheral and the GIC must be match. */
205 uint32_t type = (EXTERNAL_IRQ_TRIG_LEVEL_LOW == p_cfg->trigger) ? 0U : 1U;
206 R_BSP_IrqDetectTypeSet(p_instance_ctrl->irq, type);
207
208 R_BSP_IrqCfg(p_instance_ctrl->irq, p_cfg->ipl, p_instance_ctrl);
209 }
210
211 /* Mark the control block as open */
212 p_instance_ctrl->open = ICU_OPEN;
213
214 return FSP_SUCCESS;
215 }
216
217 /*******************************************************************************************************************//**
218 * Enable external interrupt for specified channel. Implements @ref external_irq_api_t::enable.
219 *
220 * @retval FSP_SUCCESS Interrupt Enabled successfully.
221 * @retval FSP_ERR_ASSERTION The p_instance_ctrl parameter was null.
222 * @retval FSP_ERR_NOT_OPEN The channel is not opened.
223 * @retval FSP_ERR_IRQ_BSP_DISABLED Requested IRQ is not defined in this system
224 **********************************************************************************************************************/
R_ICU_ExternalIrqEnable(external_irq_ctrl_t * const p_ctrl)225 fsp_err_t R_ICU_ExternalIrqEnable (external_irq_ctrl_t * const p_ctrl)
226 {
227 icu_instance_ctrl_t * p_instance_ctrl = (icu_instance_ctrl_t *) p_ctrl;
228
229 #if ICU_CFG_PARAM_CHECKING_ENABLE
230 FSP_ASSERT(NULL != p_instance_ctrl);
231 FSP_ERROR_RETURN(ICU_OPEN == p_instance_ctrl->open, FSP_ERR_NOT_OPEN);
232 FSP_ERROR_RETURN(p_instance_ctrl->irq >= 0, FSP_ERR_IRQ_BSP_DISABLED);
233 #endif
234
235 /* Clear the interrupt status and Pending bits, before the interrupt is enabled. */
236 R_BSP_IrqEnable(p_instance_ctrl->irq);
237
238 return FSP_SUCCESS;
239 }
240
241 /*******************************************************************************************************************//**
242 * Disable external interrupt for specified channel. Implements @ref external_irq_api_t::disable.
243 *
244 * @retval FSP_SUCCESS Interrupt disabled successfully.
245 * @retval FSP_ERR_ASSERTION The p_instance_ctrl parameter was null.
246 * @retval FSP_ERR_NOT_OPEN The channel is not opened.
247 * @retval FSP_ERR_IRQ_BSP_DISABLED Requested IRQ is not defined in this system
248 **********************************************************************************************************************/
R_ICU_ExternalIrqDisable(external_irq_ctrl_t * const p_ctrl)249 fsp_err_t R_ICU_ExternalIrqDisable (external_irq_ctrl_t * const p_ctrl)
250 {
251 icu_instance_ctrl_t * p_instance_ctrl = (icu_instance_ctrl_t *) p_ctrl;
252
253 #if ICU_CFG_PARAM_CHECKING_ENABLE
254 FSP_ASSERT(NULL != p_instance_ctrl);
255 FSP_ERROR_RETURN(ICU_OPEN == p_instance_ctrl->open, FSP_ERR_NOT_OPEN);
256 FSP_ERROR_RETURN(p_instance_ctrl->irq >= 0, FSP_ERR_IRQ_BSP_DISABLED);
257 #endif
258
259 /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
260 R_BSP_IrqDisable(p_instance_ctrl->irq);
261
262 return FSP_SUCCESS;
263 }
264
265 /*******************************************************************************************************************//**
266 * Updates the user callback and has option of providing memory for callback structure.
267 * Implements external_irq_api_t::callbackSet
268 *
269 * @retval FSP_SUCCESS Callback updated successfully.
270 * @retval FSP_ERR_ASSERTION A required pointer is NULL.
271 * @retval FSP_ERR_NOT_OPEN The control block has not been opened.
272 **********************************************************************************************************************/
R_ICU_ExternalIrqCallbackSet(external_irq_ctrl_t * const p_ctrl,void (* p_callback)(external_irq_callback_args_t *),void const * const p_context,external_irq_callback_args_t * const p_callback_memory)273 fsp_err_t R_ICU_ExternalIrqCallbackSet (external_irq_ctrl_t * const p_ctrl,
274 void ( * p_callback)(
275 external_irq_callback_args_t *),
276 void const * const p_context,
277 external_irq_callback_args_t * const p_callback_memory)
278 {
279 icu_instance_ctrl_t * p_instance_ctrl = p_ctrl;
280
281 #if ICU_CFG_PARAM_CHECKING_ENABLE
282 FSP_ASSERT(NULL != p_instance_ctrl);
283 FSP_ERROR_RETURN(ICU_OPEN == p_instance_ctrl->open, FSP_ERR_NOT_OPEN);
284 FSP_ASSERT(NULL != p_callback);
285 #endif
286
287 FSP_PARAMETER_NOT_USED(p_callback_memory);
288
289 /* Store callback and context */
290 p_instance_ctrl->p_callback = p_callback;
291 p_instance_ctrl->p_context = p_context;
292
293 return FSP_SUCCESS;
294 }
295
296 /*******************************************************************************************************************//**
297 * Close the external interrupt channel. Implements @ref external_irq_api_t::close.
298 *
299 * @retval FSP_SUCCESS Successfully closed.
300 * @retval FSP_ERR_ASSERTION The parameter p_instance_ctrl is NULL.
301 * @retval FSP_ERR_NOT_OPEN The channel is not opened.
302 **********************************************************************************************************************/
R_ICU_ExternalIrqClose(external_irq_ctrl_t * const p_ctrl)303 fsp_err_t R_ICU_ExternalIrqClose (external_irq_ctrl_t * const p_ctrl)
304 {
305 icu_instance_ctrl_t * p_instance_ctrl = (icu_instance_ctrl_t *) p_ctrl;
306
307 #if ICU_CFG_PARAM_CHECKING_ENABLE
308 FSP_ASSERT(NULL != p_instance_ctrl);
309 FSP_ERROR_RETURN(ICU_OPEN == p_instance_ctrl->open, FSP_ERR_NOT_OPEN);
310 #endif
311
312 /* Cleanup. Disable interrupt */
313 if (p_instance_ctrl->irq >= 0)
314 {
315 /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
316 R_BSP_IrqDisable(p_instance_ctrl->irq);
317 R_FSP_IsrContextSet(p_instance_ctrl->irq, NULL);
318 }
319
320 p_instance_ctrl->open = 0U;
321
322 return FSP_SUCCESS;
323 }
324
325 /*******************************************************************************************************************//**
326 * @} (end addtogroup ICU)
327 **********************************************************************************************************************/
328
329 /*******************************************************************************************************************//**
330 * ICU External Interrupt ISR.
331 **********************************************************************************************************************/
r_icu_isr(void)332 void r_icu_isr (void)
333 {
334 ICU_CFG_MULTIPLEX_INTERRUPT_ENABLE;
335
336 /* Save context if RTOS is used */
337 FSP_CONTEXT_SAVE;
338
339 IRQn_Type irq = R_FSP_CurrentIrqGet();
340 icu_instance_ctrl_t * p_instance_ctrl = (icu_instance_ctrl_t *) R_FSP_IsrContextGet(irq);
341
342 if ((NULL != p_instance_ctrl) && (NULL != p_instance_ctrl->p_callback))
343 {
344 /* Set data to identify callback to user, then call user callback. */
345 external_irq_callback_args_t args;
346 args.channel = p_instance_ctrl->channel;
347 args.p_context = p_instance_ctrl->p_context;
348 p_instance_ctrl->p_callback(&args);
349 }
350
351 /* Restore context if RTOS is used */
352 FSP_CONTEXT_RESTORE;
353
354 ICU_CFG_MULTIPLEX_INTERRUPT_DISABLE;
355 }
356