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