1 /***************************************************************************//**
2  * @file
3  * @brief Power Manager execution modes API implementation.
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include "em_device.h"
32 #include "sl_power_manager_config.h"
33 #include "sl_power_manager.h"
34 #include "sli_power_manager.h"
35 #include "sl_power_manager_execution_modes.h"
36 #include "sli_power_manager_execution_modes_private.h"
37 #include "sli_clock_manager.h"
38 #include "sl_assert.h"
39 #include "sl_core.h"
40 
41 #include <stdlib.h>
42 #include <stdint.h>
43 
44 /*******************************************************************************
45  ***************************  LOCAL VARIABLES   ********************************
46  ******************************************************************************/
47 
48 #if defined(SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN) && (SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN == 1)
49 // Counter that indicates the presence (not zero) or absence (zero) of
50 // requirements on the performance execution mode.
51 static uint8_t performance_mode_requirement = 0;
52 
53 static sl_oscillator_t current_sysclk_source_clock;
54 #endif
55 
56 /*******************************************************************************
57  **************************   GLOBAL FUNCTIONS   *******************************
58  ******************************************************************************/
59 
60 extern __INLINE void sl_power_manager_add_performance_mode_requirement(void);
61 extern __INLINE void sl_power_manager_remove_performance_mode_requirement(void);
62 
63 /***************************************************************************//**
64  * Updates requirement on the given energy mode.
65  *
66  * @param   add   Flag indicating if requirement is added (true) or removed
67  *                (false).
68  ******************************************************************************/
sli_power_manager_update_execution_mode_requirement(bool add)69 void sli_power_manager_update_execution_mode_requirement(bool add)
70 {
71 #if defined(SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN) && (SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN == 1)
72   CORE_DECLARE_IRQ_STATE;
73 
74   CORE_ENTER_CRITICAL();
75   // Cannot increment above 255 (wraparound not allowed)
76   EFM_ASSERT(!((performance_mode_requirement == UINT8_MAX) && (add == true)));
77   // Cannot decrement below 0 (wraparound not allowed)
78   EFM_ASSERT(!((performance_mode_requirement == 0) && (add == false)));
79 
80   // Increment (add) or decrement (remove) energy mode counter.
81   performance_mode_requirement += (add) ? 1 : -1;
82 
83   // Update SYSCLK based on requirements if current energy mode is EM0
84   if (sli_power_manager_get_current_em() == SL_POWER_MANAGER_EM0) {
85     if (performance_mode_requirement == 1) {
86       sli_power_manager_hal_apply_performance_mode();
87     } else if (performance_mode_requirement == 0) {
88       sli_power_manager_hal_apply_standard_mode();
89     }
90   }
91   CORE_EXIT_CRITICAL();
92 #else
93   (void)add;
94 #endif
95 }
96 
97 #if defined(SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN) && (SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN == 1)
98 /***************************************************************************//**
99  * Initializes execution mode feature.
100  ******************************************************************************/
sli_power_manager_executions_modes_init(void)101 void sli_power_manager_executions_modes_init(void)
102 {
103   sli_power_manager_executions_modes_hal_init();
104 }
105 #endif
106 
107 #if defined(SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN) && (SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN == 1)
108 /***************************************************************************//**
109  * Implement execution mode if not already implemented during a wakeup event.
110  ******************************************************************************/
sli_power_manager_implement_execution_mode_on_wakeup(void)111 void sli_power_manager_implement_execution_mode_on_wakeup(void)
112 {
113   CORE_DECLARE_IRQ_STATE;
114 
115   CORE_ENTER_CRITICAL();
116   sli_clock_manager_get_sysclk_source(&current_sysclk_source_clock);
117   if ((performance_mode_requirement >= 1) && (current_sysclk_source_clock != SL_OSCILLATOR_SOCPLL)) {
118     // If there are performance mode requiremets but SYSCLK has not been updated to performance mode, peform update after entering EM0.
119     sli_power_manager_hal_apply_performance_mode();
120   } else if (performance_mode_requirement == 0) {
121     // If there are no peformance mode requirements, but SYSCLK has not been switched to standard mode, peform update after entering EM0.
122     sli_power_manager_hal_apply_standard_mode();
123   }
124   CORE_EXIT_CRITICAL();
125 }
126 #endif
127 
128 #if defined(SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN) && (SL_POWER_MANAGER_EXECUTION_MODES_FEATURE_EN == 1)
129 /***************************************************************************//**
130  * When this callback function is called, it means that HFXO is ready and
131  * notifies the power manager that the execution mode can be updated to
132  * performance mode.
133  ******************************************************************************/
sli_clock_manager_notify_hfxo_ready(void)134 void sli_clock_manager_notify_hfxo_ready(void)
135 {
136   CORE_DECLARE_IRQ_STATE;
137 
138   CORE_ENTER_CRITICAL();
139   if ((performance_mode_requirement >= 1) && (current_sysclk_source_clock != SL_OSCILLATOR_SOCPLL)) {
140     sl_status_t status = sli_clock_manager_set_sysclk_source(SL_OSCILLATOR_SOCPLL);
141     EFM_ASSERT(status == SL_STATUS_OK);
142   }
143   // Remove HFXO FORCEEN.
144   HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN;
145   CORE_EXIT_CRITICAL();
146 }
147 #endif
148