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