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_intc_irq.h"
11 #include "r_intc_irq_cfg.h"
12 
13 /***********************************************************************************************************************
14  * Macro definitions
15  **********************************************************************************************************************/
16 
17 /** "IRQ" in ASCII, used to determine if channel is open. */
18 #define INTC_IRQ_OPEN                (0x0000495251U)
19 
20 #define INTC_IRQ_TRIG_LEVEL_LOW      (0U)
21 #define INTC_IRQ_TRIG_FALLING        (1U)
22 #define INTC_IRQ_TRIG_RISING         (2U)
23 #define INTC_IRQ_TRIG_BOTH_EDGE      (3U)
24 
25 #define INTC_IRQ_IITSR_IITSEL_MASK   (3U)
26 #define INTC_IRQ_IITSR_IITSEL_WIDTH  (2U)
27 
28 #define INTC_IRQ_ISCR_ISTAT_MASK     (1U)
29 
30 /***********************************************************************************************************************
31  * Typedef definitions
32  **********************************************************************************************************************/
33 
34 #if defined(__ARMCC_VERSION) || defined(__ICCARM__)
35 typedef void (BSP_CMSE_NONSECURE_CALL * intc_irq_prv_ns_callback)(external_irq_callback_args_t * p_args);
36 #elif defined(__GNUC__)
37 typedef BSP_CMSE_NONSECURE_CALL void (*volatile intc_irq_prv_ns_callback)(external_irq_callback_args_t * p_args);
38 #endif
39 
40 /***********************************************************************************************************************
41  * Private function prototypes
42  **********************************************************************************************************************/
43 void r_intc_irq_isr(void);
44 
45 /***********************************************************************************************************************
46  * Private global variables
47  **********************************************************************************************************************/
48 
49 /***********************************************************************************************************************
50  * Global Variables
51  **********************************************************************************************************************/
52 
53 /* INTC_IRQ implementation of External IRQ API. */
54 const external_irq_api_t g_external_irq_on_intc_irq =
55 {
56     .open        = R_INTC_IRQ_ExternalIrqOpen,
57     .enable      = R_INTC_IRQ_ExternalIrqEnable,
58     .disable     = R_INTC_IRQ_ExternalIrqDisable,
59     .callbackSet = R_INTC_IRQ_ExternalIrqCallbackSet,
60     .close       = R_INTC_IRQ_ExternalIrqClose,
61 };
62 
63 /*******************************************************************************************************************//**
64  * @addtogroup INTC_IRQ
65  * @{
66  **********************************************************************************************************************/
67 
68 /***********************************************************************************************************************
69  * Functions
70  **********************************************************************************************************************/
71 
72 /*******************************************************************************************************************//**
73  * Configure an IRQ input pin for use with the external interrupt interface. Implements @ref external_irq_api_t::open.
74  *
75  * The Open function is responsible for preparing an external IRQ pin for operation.
76  *
77  * @retval FSP_SUCCESS                    Open successful.
78  * @retval FSP_ERR_ASSERTION              One of the following is invalid:
79  *                                          - p_ctrl or p_cfg is NULL
80  * @retval FSP_ERR_ALREADY_OPEN           The channel specified has already been opened. No configurations were changed.
81  *                                        Call the associated Close function to reconfigure the channel.
82  * @retval FSP_ERR_IP_CHANNEL_NOT_PRESENT The channel requested in p_cfg is not available on the device selected in
83  *                                        r_bsp_cfg.h.
84  * @note This function is reentrant for different channels. It is not reentrant for the same channel.
85  **********************************************************************************************************************/
R_INTC_IRQ_ExternalIrqOpen(external_irq_ctrl_t * const p_api_ctrl,external_irq_cfg_t const * const p_cfg)86 fsp_err_t R_INTC_IRQ_ExternalIrqOpen (external_irq_ctrl_t * const p_api_ctrl, external_irq_cfg_t const * const p_cfg)
87 {
88     intc_irq_instance_ctrl_t * p_ctrl = (intc_irq_instance_ctrl_t *) p_api_ctrl;
89 
90 #if INTC_IRQ_CFG_PARAM_CHECKING_ENABLE
91     FSP_ASSERT(NULL != p_ctrl);
92     FSP_ERROR_RETURN(INTC_IRQ_OPEN != p_ctrl->open, FSP_ERR_ALREADY_OPEN);
93     FSP_ASSERT(NULL != p_cfg);
94     FSP_ERROR_RETURN(0 != ((1U << p_cfg->channel) & BSP_FEATURE_INTC_IRQ_VALID_CHANNEL_MASK), FSP_ERR_IP_CHANNEL_NOT_PRESENT);
95 #endif
96 
97     p_ctrl->irq = p_cfg->irq;
98 
99 #if BSP_TZ_SECURE_BUILD
100 
101     /* If this is a secure build, the callback provided in p_cfg must be secure. */
102     p_ctrl->p_callback_memory = NULL;
103 #endif
104 
105     /* Initialize control block. */
106     p_ctrl->p_callback = p_cfg->p_callback;
107     p_ctrl->p_context  = p_cfg->p_context;
108     p_ctrl->channel    = p_cfg->channel;
109 
110     uint32_t trigger = 0;
111     /* Convert the trigger. */
112     if (EXTERNAL_IRQ_TRIG_LEVEL_LOW == p_cfg->trigger)
113     {
114         trigger = INTC_IRQ_TRIG_LEVEL_LOW;
115     }
116     else if (EXTERNAL_IRQ_TRIG_FALLING == p_cfg->trigger)
117     {
118         trigger = INTC_IRQ_TRIG_FALLING;
119     }
120     else if (EXTERNAL_IRQ_TRIG_RISING == p_cfg->trigger)
121     {
122         trigger = INTC_IRQ_TRIG_RISING;
123     }
124     else if (EXTERNAL_IRQ_TRIG_BOTH_EDGE == p_cfg->trigger)
125     {
126         trigger = INTC_IRQ_TRIG_BOTH_EDGE;
127     }
128     else
129     {
130         /* Do nothing. */
131     }
132 
133     /* Set the trigger. */
134     uint32_t iitsr = R_INTC_IM33->IITSR;
135     iitsr &= ~(INTC_IRQ_IITSR_IITSEL_MASK << (p_ctrl->channel * INTC_IRQ_IITSR_IITSEL_WIDTH));
136     iitsr |= (trigger << (p_ctrl->channel * INTC_IRQ_IITSR_IITSEL_WIDTH));
137     R_INTC_IM33->IITSR = iitsr;
138 
139     if (INTC_IRQ_TRIG_LEVEL_LOW == trigger)
140     {
141         /* Do nothing. */
142     }
143     else
144     {
145         /* Dummy read the ISCR before clearing the ISTAT bit. */
146         volatile uint32_t iscr = R_INTC_IM33->ISCR;
147         FSP_PARAMETER_NOT_USED(iscr);
148 
149         /* Clear the ISTAT bit after changing the trigger setting to the edge type.
150          * Reference section "Precaution when Changing Interrupt Settings" of the user's manual. */
151         R_INTC_IM33->ISCR = ~(INTC_IRQ_ISCR_ISTAT_MASK << p_ctrl->channel);
152 
153         /* Dummy read the ISCR to prevent the interrupt cause that have been cleared from being accidentally accepted.
154          * Reference section "Clear Timing of Interrupt Cause" of the user's manual. */
155         iscr = R_INTC_IM33->ISCR;
156         FSP_PARAMETER_NOT_USED(iscr);
157     }
158 
159     if (p_ctrl->irq >= 0)
160     {
161         R_BSP_IrqCfg(p_ctrl->irq, p_cfg->ipl, p_ctrl);
162     }
163 
164     /* Mark the control block as open. */
165     p_ctrl->open = INTC_IRQ_OPEN;
166 
167     return FSP_SUCCESS;
168 }
169 
170 /*******************************************************************************************************************//**
171  * Enable external interrupt for specified channel at NVIC. Implements @ref external_irq_api_t::enable.
172  *
173  * @retval FSP_SUCCESS                 Interrupt Enabled successfully.
174  * @retval FSP_ERR_ASSERTION           The p_ctrl parameter was null.
175  * @retval FSP_ERR_NOT_OPEN            The channel is not opened.
176  * @retval FSP_ERR_IRQ_BSP_DISABLED    Requested IRQ is not defined in this system.
177  **********************************************************************************************************************/
R_INTC_IRQ_ExternalIrqEnable(external_irq_ctrl_t * const p_api_ctrl)178 fsp_err_t R_INTC_IRQ_ExternalIrqEnable (external_irq_ctrl_t * const p_api_ctrl)
179 {
180     intc_irq_instance_ctrl_t * p_ctrl = (intc_irq_instance_ctrl_t *) p_api_ctrl;
181 
182 #if INTC_IRQ_CFG_PARAM_CHECKING_ENABLE
183     FSP_ASSERT(NULL != p_ctrl);
184     FSP_ERROR_RETURN(INTC_IRQ_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
185     FSP_ERROR_RETURN(p_ctrl->irq >= 0, FSP_ERR_IRQ_BSP_DISABLED);
186 #endif
187 
188     /* Clear the interrupt status and Pending bits, before the interrupt is enabled. */
189     R_BSP_IrqEnable(p_ctrl->irq);
190 
191     return FSP_SUCCESS;
192 }
193 
194 /*******************************************************************************************************************//**
195  * Disable external interrupt for specified channel at NVIC. Implements @ref external_irq_api_t::disable.
196  *
197  * @retval FSP_SUCCESS                 Interrupt disabled successfully.
198  * @retval FSP_ERR_ASSERTION           The p_ctrl parameter was null.
199  * @retval FSP_ERR_NOT_OPEN            The channel is not opened.
200  * @retval FSP_ERR_IRQ_BSP_DISABLED    Requested IRQ is not defined in this system.
201  **********************************************************************************************************************/
R_INTC_IRQ_ExternalIrqDisable(external_irq_ctrl_t * const p_api_ctrl)202 fsp_err_t R_INTC_IRQ_ExternalIrqDisable (external_irq_ctrl_t * const p_api_ctrl)
203 {
204     intc_irq_instance_ctrl_t * p_ctrl = (intc_irq_instance_ctrl_t *) p_api_ctrl;
205 
206 #if INTC_IRQ_CFG_PARAM_CHECKING_ENABLE
207     FSP_ASSERT(NULL != p_ctrl);
208     FSP_ERROR_RETURN(INTC_IRQ_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
209     FSP_ERROR_RETURN(p_ctrl->irq >= 0, FSP_ERR_IRQ_BSP_DISABLED);
210 #endif
211 
212     /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
213     R_BSP_IrqDisable(p_ctrl->irq);
214 
215     return FSP_SUCCESS;
216 }
217 
218 /*******************************************************************************************************************//**
219  * Updates the user callback and has option of providing memory for callback structure.
220  * Implements external_irq_api_t::callbackSet.
221  *
222  * @retval  FSP_SUCCESS                  Callback updated successfully.
223  * @retval  FSP_ERR_ASSERTION            A required pointer is NULL.
224  * @retval  FSP_ERR_NOT_OPEN             The control block has not been opened.
225  * @retval  FSP_ERR_NO_CALLBACK_MEMORY   p_callback is non-secure and p_callback_memory is either secure or NULL.
226  **********************************************************************************************************************/
R_INTC_IRQ_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)227 fsp_err_t R_INTC_IRQ_ExternalIrqCallbackSet (external_irq_ctrl_t * const p_api_ctrl,
228                                              void (                    * p_callback)(
229                                                  external_irq_callback_args_t *),
230                                              void const * const                   p_context,
231                                              external_irq_callback_args_t * const p_callback_memory)
232 {
233     intc_irq_instance_ctrl_t * p_ctrl = p_api_ctrl;
234 
235 #if BSP_TZ_SECURE_BUILD
236 
237     /* cmse_check_address_range returns NULL if p_callback is located in secure memory. */
238     bool callback_is_secure =
239         (NULL == cmse_check_address_range((void *) p_callback, sizeof(void *), CMSE_AU_NONSECURE));
240 #else
241     FSP_PARAMETER_NOT_USED(p_callback_memory);
242 #endif
243 
244 #if INTC_IRQ_CFG_PARAM_CHECKING_ENABLE
245     FSP_ASSERT(NULL != p_ctrl);
246     FSP_ERROR_RETURN(INTC_IRQ_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
247     FSP_ASSERT(NULL != p_callback);
248 
249  #if BSP_TZ_SECURE_BUILD
250 
251     /* In secure projects, p_callback_memory must be provided in non-secure space if p_callback is non-secure. */
252     external_irq_callback_args_t * const p_callback_memory_checked = cmse_check_pointed_object(p_callback_memory,
253                                                                                                CMSE_AU_NONSECURE);
254     FSP_ERROR_RETURN(callback_is_secure || (NULL != p_callback_memory_checked), FSP_ERR_NO_CALLBACK_MEMORY);
255  #endif
256 #endif
257 
258 #if BSP_TZ_SECURE_BUILD
259     p_ctrl->p_callback_memory = p_callback_memory;
260     p_ctrl->p_callback        = callback_is_secure ? p_callback :
261                                 (void (*)(external_irq_callback_args_t *))cmse_nsfptr_create(p_callback);
262 #else
263     p_ctrl->p_callback = p_callback;
264 #endif
265     p_ctrl->p_context = p_context;
266 
267     return FSP_SUCCESS;
268 }
269 
270 /*******************************************************************************************************************//**
271  * Close the external interrupt channel. Implements @ref external_irq_api_t::close.
272  *
273  * @retval     FSP_SUCCESS          Successfully closed.
274  * @retval     FSP_ERR_ASSERTION    The parameter p_ctrl is NULL.
275  * @retval     FSP_ERR_NOT_OPEN     The channel is not opened.
276  **********************************************************************************************************************/
R_INTC_IRQ_ExternalIrqClose(external_irq_ctrl_t * const p_api_ctrl)277 fsp_err_t R_INTC_IRQ_ExternalIrqClose (external_irq_ctrl_t * const p_api_ctrl)
278 {
279     intc_irq_instance_ctrl_t * p_ctrl = (intc_irq_instance_ctrl_t *) p_api_ctrl;
280 
281 #if INTC_IRQ_CFG_PARAM_CHECKING_ENABLE
282     FSP_ASSERT(NULL != p_ctrl);
283     FSP_ERROR_RETURN(INTC_IRQ_OPEN == p_ctrl->open, FSP_ERR_NOT_OPEN);
284 #endif
285 
286     /* Cleanup. Disable interrupt. */
287     if (p_ctrl->irq >= 0)
288     {
289         /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
290         R_BSP_IrqDisable(p_ctrl->irq);
291         R_FSP_IsrContextSet(p_ctrl->irq, NULL);
292     }
293 
294     p_ctrl->open = 0U;
295 
296     return FSP_SUCCESS;
297 }
298 
299 /*******************************************************************************************************************//**
300  * @} (end addtogroup INTC_IRQ)
301  **********************************************************************************************************************/
302 
303 /*******************************************************************************************************************//**
304  * INTC_IRQ External Interrupt ISR.
305  **********************************************************************************************************************/
r_intc_irq_isr(void)306 void r_intc_irq_isr (void)
307 {
308     /* Save context if RTOS is used. */
309     FSP_CONTEXT_SAVE
310 
311     IRQn_Type                  irq    = R_FSP_CurrentIrqGet();
312     intc_irq_instance_ctrl_t * p_ctrl = (intc_irq_instance_ctrl_t *) R_FSP_IsrContextGet(irq);
313 
314     /* Retrieve the trigger setting. */
315     uint32_t iitsr = R_INTC_IM33->IITSR;
316     iitsr &= (INTC_IRQ_IITSR_IITSEL_MASK << (p_ctrl->channel * INTC_IRQ_IITSR_IITSEL_WIDTH));
317     iitsr >>= (p_ctrl->channel * INTC_IRQ_IITSR_IITSEL_WIDTH);
318 
319     if (INTC_IRQ_TRIG_LEVEL_LOW == iitsr)
320     {
321         /* Do nothing. */
322     }
323     else
324     {
325         /* Dummy read the ISCR before clearing the ISTAT bit. */
326         volatile uint32_t iscr = R_INTC_IM33->ISCR;
327         FSP_PARAMETER_NOT_USED(iscr);
328 
329         /* Clear the ISTAT bit before calling the user callback so that if an edge is detected while the ISR is active
330          * it will not be missed. */
331         R_INTC_IM33->ISCR = ~(INTC_IRQ_ISCR_ISTAT_MASK << p_ctrl->channel);
332 
333         /* Dummy read the ISCR to prevent the interrupt cause that should have been cleared from being accidentally
334          * accepted again. Reference section "Clear Timing of Interrupt Cause" of the user's manual. */
335         iscr = R_INTC_IM33->ISCR;
336         FSP_PARAMETER_NOT_USED(iscr);
337     }
338 
339     if ((NULL != p_ctrl) && (NULL != p_ctrl->p_callback))
340     {
341 #if BSP_TZ_SECURE_BUILD
342 
343         /* p_callback can point to a secure function or a non-secure function. */
344         external_irq_callback_args_t args;
345         if (!cmse_is_nsfptr(p_ctrl->p_callback))
346         {
347             /* If p_callback is secure, then the project does not need to change security state. */
348             args.channel   = p_ctrl->channel;
349             args.p_context = p_ctrl->p_context;
350             p_ctrl->p_callback(&args);
351         }
352         else
353         {
354             /* Save current state of p_callback_args so that it can be shared between interrupts. */
355             args = *p_ctrl->p_callback_memory;
356 
357             /* Set the callback args passed to the Non-secure callback. */
358             p_ctrl->p_callback_memory->channel   = p_ctrl->channel;
359             p_ctrl->p_callback_memory->p_context = p_ctrl->p_context;
360 
361             /* If p_callback is Non-secure, then the project must change to Non-secure state in order to call the callback. */
362             intc_irq_prv_ns_callback p_callback = (intc_irq_prv_ns_callback) (p_ctrl->p_callback);
363             p_callback(p_ctrl->p_callback_memory);
364 
365             /* Restore the state of p_callback_args. */
366             *p_ctrl->p_callback_memory = args;
367         }
368 
369 #else
370 
371         /* Set data to identify callback to user, then call user callback. */
372         external_irq_callback_args_t args;
373         args.channel   = p_ctrl->channel;
374         args.p_context = p_ctrl->p_context;
375         p_ctrl->p_callback(&args);
376 #endif
377     }
378 
379     /* Restore context if RTOS is used. */
380     FSP_CONTEXT_RESTORE
381 }
382