1 //*****************************************************************************
2 //
3 //  am_hal_interrupt.c
4 //! @file
5 //!
6 //! @brief Helper functions supporting interrupts and NVIC operation.
7 //!
8 //! These functions may be used for NVIC-level interrupt configuration.
9 //!
10 //! @addtogroup interrupt2 Interrupt (ARM NVIC support functions)
11 //! @ingroup apollo2hal
12 //! @{
13 //
14 //*****************************************************************************
15 
16 //*****************************************************************************
17 //
18 // Copyright (c) 2017, Ambiq Micro
19 // All rights reserved.
20 //
21 // Redistribution and use in source and binary forms, with or without
22 // modification, are permitted provided that the following conditions are met:
23 //
24 // 1. Redistributions of source code must retain the above copyright notice,
25 // this list of conditions and the following disclaimer.
26 //
27 // 2. Redistributions in binary form must reproduce the above copyright
28 // notice, this list of conditions and the following disclaimer in the
29 // documentation and/or other materials provided with the distribution.
30 //
31 // 3. Neither the name of the copyright holder nor the names of its
32 // contributors may be used to endorse or promote products derived from this
33 // software without specific prior written permission.
34 //
35 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
39 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
45 // POSSIBILITY OF SUCH DAMAGE.
46 //
47 // This is part of revision 1.2.11 of the AmbiqSuite Development Package.
48 //
49 //*****************************************************************************
50 
51 #include <stdint.h>
52 #include <stdbool.h>
53 #include "am_mcu_apollo.h"
54 
55 //*****************************************************************************
56 //
57 //! @brief Enable an interrupt.
58 //!
59 //! @param ui32Interrupt The ISR number of the interrupt to be enabled.
60 //!
61 //! This function enables an interrupt signal to the NVIC based on the provided
62 //! ISR number.
63 //!
64 //! @return None
65 //
66 //*****************************************************************************
67 void
am_hal_interrupt_enable(uint32_t ui32Interrupt)68 am_hal_interrupt_enable(uint32_t ui32Interrupt)
69 {
70     //
71     // Check to see what type of interrupt this is.
72     //
73     if ( ui32Interrupt > 15 )
74     {
75         //
76         // If this ISR number corresponds to a "normal" peripheral interrupt,
77         // enable it using the NVIC register.
78         //
79         AM_REG(NVIC, ISER0) = 0x1 << ((ui32Interrupt - 16) & 0x1F);
80     }
81     else
82     {
83         //
84         // If this is an ARM internal interrupt number, route it to the
85         // appropriate enable register.
86         //
87         switch(ui32Interrupt)
88         {
89             case AM_HAL_INTERRUPT_BUSFAULT:
90                 AM_BFW(SYSCTRL, SHCSR, BUSFAULTENA, 1);
91             break;
92 
93             case AM_HAL_INTERRUPT_USAGEFAULT:
94                 AM_BFW(SYSCTRL, SHCSR, USAGEFAULTENA, 1);
95             break;
96 
97             case AM_HAL_INTERRUPT_MPUFAULT:
98                 AM_BFW(SYSCTRL, SHCSR, MEMFAULTENA, 1);
99             break;
100         }
101     }
102 }
103 
104 //*****************************************************************************
105 //
106 //! @brief Disable an interrupt.
107 //!
108 //! @param ui32Interrupt The ISR number of the interrupt to be disabled.
109 //!
110 //! This function disables an interrupt signal to the NVIC based on the
111 //! provided ISR number.
112 //!
113 //! @return None
114 //
115 //*****************************************************************************
116 void
am_hal_interrupt_disable(uint32_t ui32Interrupt)117 am_hal_interrupt_disable(uint32_t ui32Interrupt)
118 {
119     //
120     // Check to see what type of interrupt this is.
121     //
122     if ( ui32Interrupt > 15 )
123     {
124         //
125         // If this ISR number corresponds to a "normal" peripheral interrupt,
126         // disable it using the NVIC register.
127         //
128         AM_REG(NVIC, ICER0) = 0x1 << ((ui32Interrupt - 16) & 0x1F);
129     }
130     else
131     {
132         //
133         // If this is an ARM internal interrupt number, route it to the
134         // appropriate enable register.
135         //
136         switch(ui32Interrupt)
137         {
138             case AM_HAL_INTERRUPT_BUSFAULT:
139                 AM_BFW(SYSCTRL, SHCSR, BUSFAULTENA, 0);
140             break;
141 
142             case AM_HAL_INTERRUPT_USAGEFAULT:
143                 AM_BFW(SYSCTRL, SHCSR, USAGEFAULTENA, 0);
144             break;
145 
146             case AM_HAL_INTERRUPT_MPUFAULT:
147                 AM_BFW(SYSCTRL, SHCSR, MEMFAULTENA, 0);
148             break;
149         }
150     }
151 }
152 
153 //*****************************************************************************
154 //
155 //! @brief Set the priority of an interrupt vector.
156 //!
157 //! @param ui32Interrupt is the ISR number of the interrupt to change.
158 //! @param ui32Priority is the new ISR priority value.
159 //!
160 //! This function changes the priority value in the NVIC for the given
161 //! interrupt vector number.
162 //!
163 //! @return None
164 //
165 //*****************************************************************************
166 void
am_hal_interrupt_priority_set(uint32_t ui32Interrupt,uint32_t ui32Priority)167 am_hal_interrupt_priority_set(uint32_t ui32Interrupt, uint32_t ui32Priority)
168 {
169     volatile uint32_t *pui32PriorityReg;
170     volatile uint32_t ui32OldPriority;
171     uint32_t ui32Shift;
172 
173     //
174     // Find the correct priority register.
175     //
176     pui32PriorityReg = (volatile uint32_t *) AM_REG_NVIC_IPR0_O;
177     pui32PriorityReg += ((ui32Interrupt - 16) >> 2);
178 
179     //
180     // Find the correct shift value.
181     //
182     ui32Shift = (((ui32Interrupt - 16) & 0x3) * 8);
183 
184     //
185     // Mask out the old priority.
186     //
187     ui32OldPriority = *pui32PriorityReg;
188     ui32OldPriority &= ~(0xFF << ui32Shift);
189 
190     //
191     // OR in the new priority.
192     //
193     *pui32PriorityReg = ui32OldPriority | (ui32Priority << ui32Shift);
194 }
195 
196 //*****************************************************************************
197 //
198 //! @brief Set a pending interrupt bit in the NVIC (Software Interrupt)
199 //!
200 //! @param ui32Interrupt is the ISR number of the interrupt to change.
201 //!
202 //! This function sets the specified bit in the Interrupt Set Pending (ISPR0)
203 //! register. For future MCUs there may be more than one ISPR.
204 //!
205 //! @return None
206 //
207 //*****************************************************************************
am_hal_interrupt_pend_set(uint32_t ui32Interrupt)208 void am_hal_interrupt_pend_set(uint32_t ui32Interrupt)
209 {
210     //
211     // Check to see if the specified interrupt is valid for this MCU
212     //
213     if ( ui32Interrupt > AM_HAL_INTERRUPT_MAX )
214     {
215         return;
216     }
217 
218     //
219     // Check to see what type of interrupt this is.
220     //
221     if ( ui32Interrupt > 15 )
222     {
223         //
224         // If this ISR number corresponds to a "normal" peripheral interrupt,
225         // disable it using the NVIC register.
226         //
227         AM_REG(NVIC, ISPR0) = 0x1 << ((ui32Interrupt - 16) & 0x1F);
228     }
229 }
230 
231 //*****************************************************************************
232 //
233 //! @brief Clear a pending interrupt bit in the NVIC without servicing it
234 //!
235 //! @param ui32Interrupt is the ISR number of the interrupt to change.
236 //!
237 //! This function clears the specified bit in the Interrupt Clear Pending
238 //! (ICPR0) register. For future MCUs there may be more than one ICPR. This
239 //! function is useful immediately following a WFI before interrupts are
240 //! re-enabled.
241 //!
242 //! @return None
243 //
244 //*****************************************************************************
am_hal_interrupt_pend_clear(uint32_t ui32Interrupt)245 void am_hal_interrupt_pend_clear(uint32_t ui32Interrupt)
246 {
247     //
248     // Check to see if the specified interrupt is valid for this MCU
249     //
250     if ( ui32Interrupt > AM_HAL_INTERRUPT_MAX )
251     {
252         return;
253     }
254 
255     //
256     // Check to see what type of interrupt this is.
257     //
258     if ( ui32Interrupt > 15 )
259     {
260         //
261         // If this ISR number corresponds to a "normal" peripheral interrupt,
262         // disable it using the NVIC register.
263         //
264         AM_REG(NVIC, ICPR0) = 0x1 << ((ui32Interrupt - 16) & 0x1F);
265     }
266 }
267 
268 //*****************************************************************************
269 //
270 //! @brief Globally enable interrupt service routines
271 //!
272 //! This function allows interrupt signals from the NVIC to trigger ISR entry
273 //! in the CPU. This function must be called if interrupts are to be serviced
274 //! in software.
275 //!
276 //! @return 1 if interrupts were previously disabled, 0 otherwise.
277 //
278 //*****************************************************************************
279 #if defined(__GNUC_STDC_INLINE__)
280 uint32_t __attribute__((naked))
am_hal_interrupt_master_enable(void)281 am_hal_interrupt_master_enable(void)
282 {
283     __asm("    mrs     r0, PRIMASK");
284     __asm("    cpsie i");
285     __asm("    bx lr");
286 }
287 #elif defined(__ARMCC_VERSION)
288 __asm uint32_t
am_hal_interrupt_master_enable(void)289 am_hal_interrupt_master_enable(void)
290 {
291     mrs     r0, PRIMASK
292     cpsie   i
293     bx      lr
294 }
295 #elif defined(__IAR_SYSTEMS_ICC__)
296 #pragma diag_suppress = Pe940   // Suppress IAR compiler warning about missing
297                                 // return statement on a non-void function
298 __stackless uint32_t
am_hal_interrupt_master_enable(void)299 am_hal_interrupt_master_enable(void)
300 {
301     __asm("    mrs     r0, PRIMASK");
302     __asm("    cpsie i");
303     __asm("    bx lr");
304 }
305 #pragma diag_default = Pe940    // Restore IAR compiler warning
306 #endif
307 
308 //*****************************************************************************
309 //
310 //! @brief Globally disable interrupt service routines
311 //!
312 //! This function prevents interrupt signals from the NVIC from triggering ISR
313 //! entry in the CPU. This will effectively stop incoming interrupt sources
314 //! from triggering their corresponding ISRs.
315 //!
316 //! @note Any external interrupt signal that occurs while the master interrupt
317 //! disable is active will still reach the "pending" state in the NVIC, but it
318 //! will not be allowed to reach the "active" state or trigger the
319 //! corresponding ISR. Instead, these interrupts are essentially "queued" until
320 //! the next time the master interrupt enable instruction is executed. At that
321 //! time, the interrupt handlers will be executed in order of decreasing
322 //! priority.
323 //!
324 //! @return 1 if interrupts were previously disabled, 0 otherwise.
325 //
326 //*****************************************************************************
327 #if defined(__GNUC_STDC_INLINE__)
328 uint32_t __attribute__((naked))
am_hal_interrupt_master_disable(void)329 am_hal_interrupt_master_disable(void)
330 {
331     __asm("    mrs     r0, PRIMASK");
332     __asm("    cpsid i");
333     __asm("    bx lr");
334 }
335 #elif defined(__ARMCC_VERSION)
336 __asm uint32_t
am_hal_interrupt_master_disable(void)337 am_hal_interrupt_master_disable(void)
338 {
339     mrs     r0, PRIMASK
340     cpsid   i
341     bx      lr
342 }
343 #elif defined(__IAR_SYSTEMS_ICC__)
344 #pragma diag_suppress = Pe940   // Suppress IAR compiler warning about missing
345                                 // return statement on a non-void function
346 __stackless uint32_t
am_hal_interrupt_master_disable(void)347 am_hal_interrupt_master_disable(void)
348 {
349     __asm("    mrs     r0, PRIMASK");
350     __asm("    cpsid i");
351     __asm("    bx lr");
352 }
353 #pragma diag_default = Pe940    // Restore IAR compiler warning
354 #endif
355 
356 //*****************************************************************************
357 //
358 //! @brief Sets the master interrupt state based on the input.
359 //!
360 //! @param ui32InterruptState - Desired PRIMASK value.
361 //!
362 //! This function directly writes the PRIMASK register in the ARM core. A value
363 //! of 1 will disable interrupts, while a value of zero will enable them.
364 //!
365 //! This function may be used along with am_hal_interrupt_master_disable() to
366 //! implement a nesting critical section. To do this, call
367 //! am_hal_interrupt_master_disable() to start the critical section, and save
368 //! its return value. To complete the critical section, call
369 //! am_hal_interrupt_master_set() using the saved return value as \e
370 //! ui32InterruptState. This will safely restore PRIMASK to the value it
371 //! contained just before the start of the critical section.
372 //!
373 //! @return None.
374 //
375 //*****************************************************************************
376 #if defined(__GNUC_STDC_INLINE__)
377 void __attribute__((naked))
am_hal_interrupt_master_set(uint32_t ui32InterruptState)378 am_hal_interrupt_master_set(uint32_t ui32InterruptState)
379 {
380     __asm("    msr     PRIMASK, r0");
381     __asm("    bx lr");
382 }
383 #elif defined(__ARMCC_VERSION)
384 __asm void
am_hal_interrupt_master_set(uint32_t ui32InterruptState)385 am_hal_interrupt_master_set(uint32_t ui32InterruptState)
386 {
387     msr     PRIMASK, r0
388     bx      lr
389 }
390 #elif defined(__IAR_SYSTEMS_ICC__)
391 #pragma diag_suppress = Pe940   // Suppress IAR compiler warning about missing
392                                 // return statement on a non-void function
393 __stackless void
am_hal_interrupt_master_set(uint32_t ui32InterruptState)394 am_hal_interrupt_master_set(uint32_t ui32InterruptState)
395 {
396     __asm("    msr     PRIMASK, r0");
397     __asm("    bx lr");
398 }
399 #pragma diag_default = Pe940    // Restore IAR compiler warning
400 #endif
401 
402 //*****************************************************************************
403 //
404 // End Doxygen group.
405 //! @}
406 //
407 //*****************************************************************************
408