1 //*****************************************************************************
2 //
3 //! @file am_hal_sysctrl.c
4 //!
5 //! @brief Functions for interfacing with the M4F system control registers
6 //!
7 //! @addtogroup sysctrl3p SYSCTRL - System Control
8 //! @ingroup apollo3p_hal
9 //! @{
10 //
11 //*****************************************************************************
12
13 //*****************************************************************************
14 //
15 // Copyright (c) 2024, Ambiq Micro, Inc.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are met:
20 //
21 // 1. Redistributions of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
23 //
24 // 2. Redistributions in binary form must reproduce the above copyright
25 // notice, this list of conditions and the following disclaimer in the
26 // documentation and/or other materials provided with the distribution.
27 //
28 // 3. Neither the name of the copyright holder nor the names of its
29 // contributors may be used to endorse or promote products derived from this
30 // software without specific prior written permission.
31 //
32 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 // POSSIBILITY OF SUCH DAMAGE.
43 //
44 // This is part of revision release_sdk_3_2_0-dd5f40c14b of the AmbiqSuite Development Package.
45 //
46 //*****************************************************************************
47
48 #include <stdint.h>
49 #include <stdbool.h>
50 #include "am_mcu_apollo.h"
51
52 //*****************************************************************************
53 //
54 // Globals
55 //
56 //*****************************************************************************
57 //
58 //! g_am_hal_sysctrl_sleep_count is a running total of the number of times the
59 //! MCU has gone to sleep. (Wraps around at uint32_t max)
60 //
61 uint32_t g_am_hal_sysctrl_sleep_count = 0;
62
63 #ifdef AM_HAL_SYSCTRL_DEEPSLEEP_WA
64 //
65 // Execute this deepsleep function in SRAM in order to implement a power
66 // saving algorithm.
67 // Function returns 0 if the power saving was successful, 1 otherwise.
68 //
69 uint32_t SRAM_sleep[20] =
70 {
71 0xB401B538, // B538 push {r3-r5,lr}
72 // B401 push {r0}
73 0x1278F240, // F2401278 movw r2, #0x0178
74 0x0202F2C4, // F2C40202 movt r2, #0x4002
75 0xBF006813, // 6813 ldr r3, [r2, #0]
76 // BF00 nop
77 0x51B0F646, // F64651B0 movw r1, #0x6DB0
78 0x0106F2C0, // F2C00106 movt r1, #0x0006
79 0x24006011, // 6011 str r1, [r2, #0]
80 // 2400 movs r4, #0
81 0x428D6815, // 6815 ldr r5, [r2, #0]
82 // 428D cmp r5, r1
83 0x2401BF18, // BF18 it ne
84 // 2401 movne r4, #1
85 0x8F4FF3BF, // F3BF8F4F dsb
86 0xBF002000, // 2000 movs r0, #0x0000
87 // BF00 nop
88 0x70FFF6C5, // F6C570FF movt r0, #0x5FFF
89 0xBF306801, // 6801 ldr r1, [r0, #0]
90 // BF30 wfi
91 0x8F6FF3BF, // F3BF8F6F isb
92 0xBC016013, // 6013 str r3, [r2,#0]
93 // BC01 pop {r0}
94 0x019dF240, // F240019D movw r1, #0x009d
95 0x0100F6C0, // F6C00100 movt r1, #0x0800
96 0x46204788, // 4788 blx r1
97 // 4620 mov r0, r4
98 0xBF00BD38, // BD38 pop {r3-r5,pc}
99 // BF00 nop
100 0xBF00BF00
101 };
102
103 //
104 // If additional deepsleep power saving is desired, incurring the cost of
105 // a short delay on wake, the user can call am_hal_sysctrl_control() to
106 // enable the power savings. This variable holds that state.
107 // Bit0: Workaround Enabled
108 // Bit1: Good-to-go to use workaround
109 // That is, only do the workaround if g_ui32DeepsleepMinPwr == 0x03.
110 //
111 #define DSLP_WORKAROUND_DIS 0x00
112 #define DSLP_WORKAROUND_EN 0x01
113 #define DSLP_WORKAROUND_GO 0x02
114 static uint32_t g_ui32DeepsleepMinPwr = DSLP_WORKAROUND_DIS;
115
116 //
117 // After sleep, this variable will be 0 if additional power saving
118 // was successful.
119 //
120 uint32_t g_ui32SleepReturnVal = 0;
121 #endif // AM_HAL_SYSCTRL_DEEPSLEEP_WA
122
123
124 // ****************************************************************************
125 //
126 // Apply various specific commands/controls on the SYSCTRL module.
127 //
128 // ****************************************************************************
129 uint32_t
am_hal_sysctrl_control(am_hal_sysctrl_control_e eControl,void * pArgs)130 am_hal_sysctrl_control(am_hal_sysctrl_control_e eControl, void *pArgs)
131 {
132 #ifdef AM_HAL_SYSCTRL_DEEPSLEEP_WA
133 switch ( eControl )
134 {
135 case AM_HAL_SYSCTRL_CONTROL_DEEPSLEEP_MINPWR_DIS:
136 g_ui32DeepsleepMinPwr = DSLP_WORKAROUND_DIS;
137 break;
138
139 case AM_HAL_SYSCTRL_CONTROL_DEEPSLEEP_MINPWR_EN:
140 //
141 // Activate the workaround that allows minimum power consumption
142 // during deepsleep with the cost of a short delay after wake.
143 // Before enabling, make sure the trim patch that allows the
144 // workaround to function has been applied.
145 //
146 g_ui32DeepsleepMinPwr = AM_REGVAL(PATCHVER2) & 0x00000001 ?
147 DSLP_WORKAROUND_DIS : DSLP_WORKAROUND_EN;
148 break;
149
150 default:
151 return AM_HAL_STATUS_INVALID_ARG;
152 }
153
154 //
155 // Return success status.
156 //
157 return AM_HAL_STATUS_SUCCESS;
158 #else // AM_HAL_SYSCTRL_DEEPSLEEP_WA
159 switch ( eControl )
160 {
161 case AM_HAL_SYSCTRL_CONTROL_DEEPSLEEP_MINPWR_DIS:
162 return AM_HAL_STATUS_INVALID_OPERATION;
163
164 case AM_HAL_SYSCTRL_CONTROL_DEEPSLEEP_MINPWR_EN:
165 return AM_HAL_STATUS_INVALID_OPERATION;
166
167 default:
168 return AM_HAL_STATUS_INVALID_ARG;
169 }
170 #endif // AM_HAL_SYSCTRL_DEEPSLEEP_WA
171 } // am_hal_sysctrl_control()
172
173 //*****************************************************************************
174 //
175 // Place the core into sleep or deepsleep.
176 //
177 // This function puts the MCU to sleep or deepsleep depending on bSleepDeep.
178 //
179 // Valid values for bSleepDeep are:
180 // AM_HAL_SYSCTRL_SLEEP_NORMAL
181 // AM_HAL_SYSCTRL_SLEEP_DEEP
182 //
183 //*****************************************************************************
184 void
am_hal_sysctrl_sleep(bool bSleepDeep)185 am_hal_sysctrl_sleep(bool bSleepDeep)
186 {
187 bool bBurstModeSleep;
188 am_hal_burst_mode_e eBurstMode;
189
190 //
191 // Disable interrupts and save the previous interrupt state.
192 //
193 AM_CRITICAL_BEGIN
194
195 g_am_hal_sysctrl_sleep_count++;
196
197 //
198 // If Apollo3 Blue Plus rev 0 and in burst mode, must exit burst mode
199 // before going to sleep.
200 //
201 if ( am_hal_burst_mode_status() == AM_HAL_BURST_MODE )
202 {
203 bBurstModeSleep = true;
204
205 if ( (am_hal_burst_mode_disable(&eBurstMode) != AM_HAL_STATUS_SUCCESS) ||
206 (eBurstMode != AM_HAL_NORMAL_MODE) )
207 {
208 while(1);
209 }
210 #if AM_HAL_BURST_LDO_WORKAROUND
211 else
212 {
213 am_hal_pwrctrl_wa_vddf_restore();
214 }
215 #endif
216 }
217 else
218 {
219 bBurstModeSleep = false;
220 }
221
222 //
223 // If the user selected DEEPSLEEP and the TPIU is off, attempt to enter
224 // DEEP SLEEP.
225 //
226 if ( (bSleepDeep == AM_HAL_SYSCTRL_SLEEP_DEEP) &&
227 (MCUCTRL->TPIUCTRL_b.ENABLE == MCUCTRL_TPIUCTRL_ENABLE_DIS) )
228 {
229 //
230 // Retrieve the reset generator status bits
231 // This gets reset on Deep Sleep, so we take a snapshot here
232 //
233 if ( !gAmHalResetStatus )
234 {
235 gAmHalResetStatus = RSTGEN->STAT;
236 }
237
238 //
239 // do not boost core and mem voltages
240 //
241
242
243 //
244 // Prepare the core for deepsleep (write 1 to the DEEPSLEEP bit).
245 //
246 SCB->SCR |= _VAL2FLD(SCB_SCR_SLEEPDEEP, 1);
247 #ifdef AM_HAL_SYSCTRL_DEEPSLEEP_WA
248 //
249 // Set the workaround flag.
250 //
251 g_ui32DeepsleepMinPwr |= DSLP_WORKAROUND_GO;
252 #endif // AM_HAL_SYSCTRL_DEEPSLEEP_WA
253 }
254 else
255 {
256 //
257 // Prepare the core for normal sleep (write 0 to the DEEPSLEEP bit).
258 //
259 SCB->SCR &= ~_VAL2FLD(SCB_SCR_SLEEPDEEP, 1);
260 #ifdef AM_HAL_SYSCTRL_DEEPSLEEP_WA
261 //
262 // Clear the workaround flag.
263 //
264 g_ui32DeepsleepMinPwr &= ~DSLP_WORKAROUND_GO;
265 #endif // AM_HAL_SYSCTRL_DEEPSLEEP_WA
266 }
267
268 #ifdef AM_HAL_SYSCTRL_DEEPSLEEP_WA
269 if ( g_ui32DeepsleepMinPwr == (DSLP_WORKAROUND_EN | DSLP_WORKAROUND_GO) )
270 {
271 //
272 // Call the Apollo3p specific SRAM sleep routine that will minimize
273 // deep sleep current consumption. On wake a short delay is incurred.
274 //
275 uint32_t SRAMCode = (uint32_t)SRAM_sleep | 0x1;
276 uint32_t (*pFunc)(uint32_t) = (uint32_t (*)(uint32_t))SRAMCode;
277 g_ui32SleepReturnVal = (*pFunc)(FLASH_CYCLES_US(5));
278 }
279 else
280 {
281 #endif // AM_HAL_SYSCTRL_DEEPSLEEP_WA
282 //
283 // Before executing WFI, flush any buffered core and peripheral writes.
284 //
285 __DSB();
286 am_hal_sysctrl_bus_write_flush();
287
288 //
289 // Execute the sleep instruction.
290 //
291 __WFI();
292
293 //
294 // Upon wake, execute the Instruction Sync Barrier instruction.
295 //
296 __ISB();
297 #ifdef AM_HAL_SYSCTRL_DEEPSLEEP_WA
298 }
299 #endif // AM_HAL_SYSCTRL_DEEPSLEEP_WA
300
301 //
302 // Restore burst mode?
303 //
304 if ( bBurstModeSleep )
305 {
306 bBurstModeSleep = false;
307
308 if ( (am_hal_burst_mode_enable(&eBurstMode) != AM_HAL_STATUS_SUCCESS) ||
309 (eBurstMode != AM_HAL_BURST_MODE) )
310 {
311 //while(1);
312 }
313 }
314
315 //
316 // Restore the interrupt state.
317 //
318 AM_CRITICAL_END
319 }
320
321 //*****************************************************************************
322 //
323 // Enable the floating point module.
324 //
325 // Call this function to enable the ARM hardware floating point module.
326 //
327 //*****************************************************************************
328 void
am_hal_sysctrl_fpu_enable(void)329 am_hal_sysctrl_fpu_enable(void)
330 {
331 //
332 // Enable access to the FPU in both privileged and user modes.
333 // NOTE: Write 0s to all reserved fields in this register.
334 //
335 SCB->CPACR = _VAL2FLD(SCB_CPACR_CP11, 0x3) |
336 _VAL2FLD(SCB_CPACR_CP10, 0x3);
337 }
338
339 //*****************************************************************************
340 //
341 // Disable the floating point module.
342 //
343 // Call this function to disable the ARM hardware floating point module.
344 //
345 //*****************************************************************************
346 void
am_hal_sysctrl_fpu_disable(void)347 am_hal_sysctrl_fpu_disable(void)
348 {
349 //
350 // Disable access to the FPU in both privileged and user modes.
351 // NOTE: Write 0s to all reserved fields in this register.
352 //
353 SCB->CPACR = 0x00000000 &
354 ~(_VAL2FLD(SCB_CPACR_CP11, 0x3) |
355 _VAL2FLD(SCB_CPACR_CP10, 0x3));
356 }
357
358 //*****************************************************************************
359 //
360 // Enable stacking of FPU registers on exception entry.
361 //
362 // @param bLazy - Set to "true" to enable "lazy stacking".
363 //
364 // This function allows the core to save floating-point information to the
365 // stack on exception entry. Setting the bLazy option enables "lazy stacking"
366 // for interrupt handlers. Normally, mixing floating-point code and interrupt
367 // driven routines causes increased interrupt latency, because the core must
368 // save extra information to the stack upon exception entry. With the lazy
369 // stacking option enabled, the core will skip the saving of floating-point
370 // registers when possible, reducing average interrupt latency.
371 //
372 // @note At reset of the Cortex M4, the ASPEN and LSPEN bits are set to 1,
373 // enabling Lazy mode by default. Therefore this function will generally
374 // only have an affect when setting for full-context save (or when switching
375 // from full-context to lazy mode).
376 //
377 // @note See also:
378 // infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0298a/DAFGGBJD.html
379 //
380 // @note Three valid FPU context saving modes are possible.
381 // 1. Lazy ASPEN=1 LSPEN=1 am_hal_sysctrl_fpu_stacking_enable(true)
382 // and default.
383 // 2. Full-context ASPEN=1 LSPEN=0 am_hal_sysctrl_fpu_stacking_enable(false)
384 // 3. No FPU state ASPEN=0 LSPEN=0 am_hal_sysctrl_fpu_stacking_disable()
385 // 4. Invalid ASPEN=0 LSPEN=1
386 //
387 //*****************************************************************************
388 void
am_hal_sysctrl_fpu_stacking_enable(bool bLazy)389 am_hal_sysctrl_fpu_stacking_enable(bool bLazy)
390 {
391 uint32_t ui32fpccr;
392
393 //
394 // Set the requested FPU stacking mode in ISRs.
395 //
396 AM_CRITICAL_BEGIN
397 #define SYSCTRL_FPCCR_LAZY (FPU_FPCCR_ASPEN_Msk | FPU_FPCCR_LSPEN_Msk)
398 ui32fpccr = FPU->FPCCR;
399 ui32fpccr &= ~SYSCTRL_FPCCR_LAZY;
400 ui32fpccr |= (bLazy ? SYSCTRL_FPCCR_LAZY : FPU_FPCCR_ASPEN_Msk);
401 FPU->FPCCR = ui32fpccr;
402 AM_CRITICAL_END
403 }
404
405 //*****************************************************************************
406 //
407 // Disable FPU register stacking on exception entry.
408 //
409 // This function disables all stacking of floating point registers for
410 // interrupt handlers. This mode should only be used when it is absolutely
411 // known that no FPU instructions will be executed in an ISR.
412 //
413 //*****************************************************************************
414 void
am_hal_sysctrl_fpu_stacking_disable(void)415 am_hal_sysctrl_fpu_stacking_disable(void)
416 {
417 //
418 // Completely disable FPU context save on entry to ISRs.
419 //
420 AM_CRITICAL_BEGIN
421 FPU->FPCCR &= ~SYSCTRL_FPCCR_LAZY;
422 AM_CRITICAL_END
423 }
424
425 //*****************************************************************************
426 //
427 // Issue a system wide reset using the AIRCR bit in the M4 system ctrl.
428 //
429 // This function issues a system wide reset (Apollo POR level reset).
430 //
431 //*****************************************************************************
432 void
am_hal_sysctrl_aircr_reset(void)433 am_hal_sysctrl_aircr_reset(void)
434 {
435 //
436 // Set the system reset bit in the AIRCR register
437 //
438 __NVIC_SystemReset();
439 }
440
441 //*****************************************************************************
442 //
443 // End Doxygen group.
444 //! @}
445 //
446 //*****************************************************************************
447