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