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