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_IRQMD_OFFSET       (0)
21 
22 #if BSP_FEATURE_ICU_HAS_FILTER
23  #define ICU_FCLKSEL_OFFSET    (4)
24  #define ICU_FLTEN_OFFSET      (7)
25 #endif
26 
27 /***********************************************************************************************************************
28  * Typedef definitions
29  **********************************************************************************************************************/
30 
31 #if defined(__ARMCC_VERSION) || defined(__ICCARM__)
32 typedef void (BSP_CMSE_NONSECURE_CALL * icu_prv_ns_callback)(external_irq_callback_args_t * p_args);
33 #elif defined(__GNUC__)
34 typedef BSP_CMSE_NONSECURE_CALL void (*volatile icu_prv_ns_callback)(external_irq_callback_args_t * p_args);
35 #endif
36 
37 /***********************************************************************************************************************
38  * Private function prototypes
39  **********************************************************************************************************************/
40 void r_icu_isr(void);
41 
42 /***********************************************************************************************************************
43  * Private global variables
44  **********************************************************************************************************************/
45 
46 /***********************************************************************************************************************
47  * Global Variables
48  **********************************************************************************************************************/
49 
50 /* ICU implementation of External IRQ API. */
51 const external_irq_api_t g_external_irq_on_icu =
52 {
53     .open        = R_ICU_ExternalIrqOpen,
54     .enable      = R_ICU_ExternalIrqEnable,
55     .disable     = R_ICU_ExternalIrqDisable,
56     .callbackSet = R_ICU_ExternalIrqCallbackSet,
57     .close       = R_ICU_ExternalIrqClose,
58 };
59 
60 /*******************************************************************************************************************//**
61  * @addtogroup ICU
62  * @{
63  **********************************************************************************************************************/
64 
65 /***********************************************************************************************************************
66  * Functions
67  **********************************************************************************************************************/
68 
69 /*******************************************************************************************************************//**
70  * Configure an IRQ input pin for use with the external interrupt interface.  Implements @ref external_irq_api_t::open.
71  *
72  * The Open function is responsible for preparing an external IRQ pin for operation.
73  *
74  * @retval FSP_SUCCESS                    Open successful.
75  * @retval FSP_ERR_ASSERTION              One of the following is invalid:
76  *                                          - p_ctrl or p_cfg is NULL
77  * @retval FSP_ERR_ALREADY_OPEN           The channel specified has already been opened. No configurations were changed.
78  *                                        Call the associated Close function to reconfigure the channel.
79  * @retval FSP_ERR_IP_CHANNEL_NOT_PRESENT The channel requested in p_cfg is not available on the device selected in
80  *                                        r_bsp_cfg.h.
81  * @retval FSP_ERR_INVALID_ARGUMENT       p_cfg->p_callback is not NULL, but ISR is not enabled. ISR must be enabled to
82  *                                        use callback function.
83  * @retval FSP_ERR_UNSUPPORTED            An input argument is not supported by selected mode.
84  *
85  * @note This function is reentrant for different channels.  It is not reentrant for the same channel.
86  **********************************************************************************************************************/
R_ICU_ExternalIrqOpen(external_irq_ctrl_t * const p_api_ctrl,external_irq_cfg_t const * const p_cfg)87 fsp_err_t R_ICU_ExternalIrqOpen (external_irq_ctrl_t * const p_api_ctrl, external_irq_cfg_t const * const p_cfg)
88 {
89     icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
90 
91 #if ICU_CFG_PARAM_CHECKING_ENABLE
92     FSP_ASSERT(NULL != p_ctrl);
93     FSP_ERROR_RETURN(ICU_OPEN != p_ctrl->open, FSP_ERR_ALREADY_OPEN);
94 
95     FSP_ASSERT(NULL != p_cfg);
96  #if !BSP_FEATURE_ICU_HAS_FILTER
97 
98     /* Verify the configuration trigger source is correct */
99     FSP_ERROR_RETURN((EXTERNAL_IRQ_TRIG_FALLING == p_cfg->trigger) ||
100                      (EXTERNAL_IRQ_TRIG_RISING == p_cfg->trigger) ||
101                      (EXTERNAL_IRQ_TRIG_BOTH_EDGE == p_cfg->trigger),
102                      FSP_ERR_UNSUPPORTED);
103  #endif
104 
105     FSP_ERROR_RETURN(0 != ((1U << p_cfg->channel) & BSP_FEATURE_ICU_IRQ_CHANNELS_MASK), FSP_ERR_IP_CHANNEL_NOT_PRESENT);
106 
107     /* Callback must be used with a valid interrupt priority otherwise it will never be called. */
108     if (p_cfg->p_callback)
109     {
110         FSP_ERROR_RETURN(BSP_IRQ_DISABLED != p_cfg->ipl, FSP_ERR_INVALID_ARGUMENT);
111     }
112 #endif
113 
114     p_ctrl->irq = p_cfg->irq;
115 
116 #if BSP_FEATURE_ICU_HAS_IELSR
117 
118     /* IELSR Must be zero when modifying the IRQCR bits.
119      * (See ICU Section 14.2.1 of the RA6M3 manual R01UH0886EJ0100). */
120     uint32_t ielsr = R_ICU->IELSR[p_ctrl->irq];
121     R_ICU->IELSR[p_ctrl->irq] = 0;
122 #endif
123 
124 #if BSP_TZ_SECURE_BUILD
125 
126     /* If this is a secure build, the callback provided in p_cfg must be secure. */
127     p_ctrl->p_callback_memory = NULL;
128 #endif
129 
130     /* Initialize control block. */
131     p_ctrl->p_callback = p_cfg->p_callback;
132     p_ctrl->p_context  = p_cfg->p_context;
133     p_ctrl->channel    = p_cfg->channel;
134 
135 #if BSP_FEATURE_ICU_HAS_FILTER
136 
137     /* Disable digital filter */
138     R_ICU->IRQCR[p_ctrl->channel] = 0U;
139 
140     /* Set the digital filter divider. */
141     uint8_t irqcr = (uint8_t) (p_cfg->clock_source_div << ICU_FCLKSEL_OFFSET);
142 
143     /* Enable/Disable digital filter. */
144     irqcr |= (uint8_t) (p_cfg->filter_enable << ICU_FLTEN_OFFSET);
145 
146     /* Set the IRQ trigger. */
147     irqcr |= (uint8_t) (p_cfg->trigger << ICU_IRQMD_OFFSET);
148 #else
149     uint8_t irqcr = (uint8_t) (p_cfg->trigger << ICU_IRQMD_OFFSET);
150 #endif
151 
152     /* Write IRQCR */
153     R_ICU->IRQCR[p_ctrl->channel] = irqcr;
154 
155 #if BSP_FEATURE_ICU_HAS_IELSR
156 
157     /* Restore IELSR. */
158     R_ICU->IELSR[p_ctrl->irq] = ielsr;
159 #endif
160 
161     /* NOTE: User can have the driver opened when the IRQ is not in the vector table. This is for use cases
162      * where the external IRQ driver is used to generate ELC events only (without CPU interrupts).
163      * In such cases we will not set the IRQ priority but will continue with the processing.
164      */
165     if (p_ctrl->irq >= 0)
166     {
167         R_BSP_IrqCfg(p_ctrl->irq, p_cfg->ipl, p_ctrl);
168     }
169 
170     /* Mark the control block as open */
171     p_ctrl->open = ICU_OPEN;
172 
173     return FSP_SUCCESS;
174 }
175 
176 /*******************************************************************************************************************//**
177  * Enable external interrupt for specified channel at NVIC. Implements @ref external_irq_api_t::enable.
178  *
179  * @retval FSP_SUCCESS                 Interrupt Enabled successfully.
180  * @retval FSP_ERR_ASSERTION           The p_ctrl parameter was null.
181  * @retval FSP_ERR_NOT_OPEN            The channel is not opened.
182  * @retval FSP_ERR_IRQ_BSP_DISABLED    Requested IRQ is not defined in this system
183  **********************************************************************************************************************/
R_ICU_ExternalIrqEnable(external_irq_ctrl_t * const p_api_ctrl)184 fsp_err_t R_ICU_ExternalIrqEnable (external_irq_ctrl_t * const p_api_ctrl)
185 {
186     icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
187 
188 #if ICU_CFG_PARAM_CHECKING_ENABLE
189     FSP_ASSERT(NULL != p_ctrl);
190     FSP_ERROR_RETURN(ICU_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
191     FSP_ERROR_RETURN(p_ctrl->irq >= 0, FSP_ERR_IRQ_BSP_DISABLED);
192 #endif
193 
194     /* Clear the interrupt status and Pending bits, before the interrupt is enabled. */
195     R_BSP_IrqEnable(p_ctrl->irq);
196 
197     return FSP_SUCCESS;
198 }
199 
200 /*******************************************************************************************************************//**
201  * Disable external interrupt for specified channel at NVIC. Implements @ref external_irq_api_t::disable.
202  *
203  * @retval FSP_SUCCESS                 Interrupt disabled successfully.
204  * @retval FSP_ERR_ASSERTION           The p_ctrl parameter was null.
205  * @retval FSP_ERR_NOT_OPEN            The channel is not opened.
206  * @retval FSP_ERR_IRQ_BSP_DISABLED    Requested IRQ is not defined in this system
207  **********************************************************************************************************************/
R_ICU_ExternalIrqDisable(external_irq_ctrl_t * const p_api_ctrl)208 fsp_err_t R_ICU_ExternalIrqDisable (external_irq_ctrl_t * const p_api_ctrl)
209 {
210     icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
211 
212 #if ICU_CFG_PARAM_CHECKING_ENABLE
213     FSP_ASSERT(NULL != p_ctrl);
214     FSP_ERROR_RETURN(ICU_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
215     FSP_ERROR_RETURN(p_ctrl->irq >= 0, FSP_ERR_IRQ_BSP_DISABLED);
216 #endif
217 
218     /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
219     R_BSP_IrqDisable(p_ctrl->irq);
220 
221     return FSP_SUCCESS;
222 }
223 
224 /*******************************************************************************************************************//**
225  * Updates the user callback and has option of providing memory for callback structure.
226  * Implements external_irq_api_t::callbackSet
227  *
228  * @retval  FSP_SUCCESS                  Callback updated successfully.
229  * @retval  FSP_ERR_ASSERTION            A required pointer is NULL.
230  * @retval  FSP_ERR_NOT_OPEN             The control block has not been opened.
231  * @retval  FSP_ERR_NO_CALLBACK_MEMORY   p_callback is non-secure and p_callback_memory is either secure or NULL.
232  **********************************************************************************************************************/
R_ICU_ExternalIrqCallbackSet(external_irq_ctrl_t * const p_api_ctrl,void (* p_callback)(external_irq_callback_args_t *),void const * const p_context,external_irq_callback_args_t * const p_callback_memory)233 fsp_err_t R_ICU_ExternalIrqCallbackSet (external_irq_ctrl_t * const p_api_ctrl,
234                                         void (                    * p_callback)(
235                                             external_irq_callback_args_t *),
236                                         void const * const                   p_context,
237                                         external_irq_callback_args_t * const p_callback_memory)
238 {
239     icu_instance_ctrl_t * p_ctrl = p_api_ctrl;
240 
241 #if BSP_TZ_SECURE_BUILD
242 
243     /* cmse_check_address_range returns NULL if p_callback is located in secure memory */
244     bool callback_is_secure =
245         (NULL == cmse_check_address_range((void *) p_callback, sizeof(void *), CMSE_AU_NONSECURE));
246 #else
247     FSP_PARAMETER_NOT_USED(p_callback_memory);
248 #endif
249 
250 #if ICU_CFG_PARAM_CHECKING_ENABLE
251     FSP_ASSERT(NULL != p_ctrl);
252     FSP_ERROR_RETURN(ICU_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
253     FSP_ASSERT(NULL != p_callback);
254 
255  #if BSP_TZ_SECURE_BUILD
256 
257     /* In secure projects, p_callback_memory must be provided in non-secure space if p_callback is non-secure */
258     external_irq_callback_args_t * const p_callback_memory_checked = cmse_check_pointed_object(p_callback_memory,
259                                                                                                CMSE_AU_NONSECURE);
260     FSP_ERROR_RETURN(callback_is_secure || (NULL != p_callback_memory_checked), FSP_ERR_NO_CALLBACK_MEMORY);
261  #endif
262 #endif
263 
264 #if BSP_TZ_SECURE_BUILD
265     p_ctrl->p_callback_memory = p_callback_memory;
266     p_ctrl->p_callback        = callback_is_secure ? p_callback :
267                                 (void (*)(external_irq_callback_args_t *))cmse_nsfptr_create(p_callback);
268 #else
269     p_ctrl->p_callback = p_callback;
270 #endif
271     p_ctrl->p_context = p_context;
272 
273     return FSP_SUCCESS;
274 }
275 
276 /*******************************************************************************************************************//**
277  * Close the external interrupt channel. Implements @ref external_irq_api_t::close.
278  *
279  * @retval     FSP_SUCCESS          Successfully closed.
280  * @retval     FSP_ERR_ASSERTION    The parameter p_ctrl is NULL.
281  * @retval     FSP_ERR_NOT_OPEN     The channel is not opened.
282  **********************************************************************************************************************/
R_ICU_ExternalIrqClose(external_irq_ctrl_t * const p_api_ctrl)283 fsp_err_t R_ICU_ExternalIrqClose (external_irq_ctrl_t * const p_api_ctrl)
284 {
285     icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
286 
287 #if ICU_CFG_PARAM_CHECKING_ENABLE
288     FSP_ASSERT(NULL != p_ctrl);
289     FSP_ERROR_RETURN(ICU_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
290 #endif
291 
292     /* Cleanup. Disable interrupt */
293     if (p_ctrl->irq >= 0)
294     {
295         /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
296         R_BSP_IrqDisable(p_ctrl->irq);
297         R_FSP_IsrContextSet(p_ctrl->irq, NULL);
298     }
299 
300     p_ctrl->open = 0U;
301 
302     return FSP_SUCCESS;
303 }
304 
305 /*******************************************************************************************************************//**
306  * @} (end addtogroup ICU)
307  **********************************************************************************************************************/
308 
309 /*******************************************************************************************************************//**
310  * ICU External Interrupt ISR.
311  **********************************************************************************************************************/
r_icu_isr(void)312 void r_icu_isr (void)
313 {
314     /* Save context if RTOS is used */
315     FSP_CONTEXT_SAVE
316 
317     IRQn_Type             irq    = R_FSP_CurrentIrqGet();
318     icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) R_FSP_IsrContextGet(irq);
319 
320 #if BSP_FEATURE_ICU_HAS_IELSR
321     bool level_irq = false;
322     if (EXTERNAL_IRQ_TRIG_LEVEL_LOW == R_ICU->IRQCR_b[p_ctrl->channel].IRQMD)
323     {
324         level_irq = true;
325     }
326     else
327     {
328         /* Clear the IR bit before calling the user callback so that if an edge is detected while the ISR is active
329          * it will not be missed. */
330         R_BSP_IrqStatusClear(irq);
331     }
332 #endif
333 
334     if ((NULL != p_ctrl) && (NULL != p_ctrl->p_callback))
335     {
336 #if BSP_TZ_SECURE_BUILD
337 
338         /* p_callback can point to a secure function or a non-secure function. */
339         external_irq_callback_args_t args;
340         if (!cmse_is_nsfptr(p_ctrl->p_callback))
341         {
342             /* If p_callback is secure, then the project does not need to change security state. */
343             args.channel   = p_ctrl->channel;
344             args.p_context = p_ctrl->p_context;
345             p_ctrl->p_callback(&args);
346         }
347         else
348         {
349             /* Save current state of p_callback_args so that it can be shared between interrupts. */
350             args = *p_ctrl->p_callback_memory;
351 
352             /* Set the callback args passed to the Non-secure calback. */
353             p_ctrl->p_callback_memory->channel   = p_ctrl->channel;
354             p_ctrl->p_callback_memory->p_context = p_ctrl->p_context;
355 
356             /* If p_callback is Non-secure, then the project must change to Non-secure state in order to call the callback. */
357             icu_prv_ns_callback p_callback = (icu_prv_ns_callback) (p_ctrl->p_callback);
358             p_callback(p_ctrl->p_callback_memory);
359 
360             /* Restore the state of p_callback_args. */
361             *p_ctrl->p_callback_memory = args;
362         }
363 
364 #else
365 
366         /* Set data to identify callback to user, then call user callback. */
367         external_irq_callback_args_t args;
368         args.channel   = p_ctrl->channel;
369         args.p_context = p_ctrl->p_context;
370         p_ctrl->p_callback(&args);
371 #endif
372     }
373 
374 #if BSP_FEATURE_ICU_HAS_IELSR
375     if (level_irq)
376     {
377         /* Clear the IR bit after calling the user callback so that if the condition is cleared the ISR will not
378          * be called again. */
379         R_BSP_IrqStatusClear(irq);
380     }
381 #endif
382 
383     /* Restore context if RTOS is used */
384     FSP_CONTEXT_RESTORE
385 }
386