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