1 /*
2 * Copyright (C) 2018 Alibaba Group Holding Limited
3 */
4
5 /*
6 DESCRIPTION
7 This library provides the support for the STM32L496G-DISCOVERY
8 CPU power state control.
9
10 CPU power management:
11 provides low-level interface for setting CPU C-states.
12 provides low-level interface for setting CPU P-states.
13 */
14
15 #include <k_api.h>
16 #include "ameba_soc.h"
17
18 #if (AOS_COMP_PWRMGMT > 0)
19
20 #include <cpu_pwr.h>
21 #include <cpu_pwr_hal_lib.h>
22 #include <pwrmgmt_debug.h>
23 #include <cpu_tickless.h>
24
25 #include "pwrmgmt_api.h"
26
27 /* forward declarations */
28 extern one_shot_timer_t rtc_one_shot; /* wakeup source for C3,C4 */
29
30 extern uint64_t expeted_sleep_ms;
31 static cpu_pwr_t cpu_pwr_node_core_0;
32
33 KM4SLEEP_ParamDef sleep_param;
34 /* psm dd hook info */
35 PSM_DD_HOOK_INFO gPsmDdHookInfo[PMU_MAX];
36
37 extern uint32_t sleep_type; /* 0 is power gate, 1 is clock gate */
38 static u32 system_can_yield = 1; /* default is can */
39 static uint32_t deepwakelock = DEFAULT_DEEP_WAKELOCK;
40
sleep_ex_cg(uint32_t sleep_duration)41 void sleep_ex_cg(uint32_t sleep_duration)
42 {
43 if (deepwakelock == 0) {
44 sleep_param.dlps_enable = ENABLE;
45 sleep_param.sleep_time = 0;
46 } else {
47 sleep_param.dlps_enable = DISABLE;
48 sleep_param.sleep_time = sleep_duration;
49 }
50
51 sleep_param.sleep_type = sleep_type;
52
53 IPCM0_DEV->IPCx_USR[IPC_INT_CHAN_SHELL_SWITCH] = 0x00000000;
54 InterruptDis(UART_LOG_IRQ);
55
56 if (sleep_type == SLEEP_PG) {
57 SOCPS_SleepPG();
58 } else {
59 SOCPS_SleepCG();
60 }
61 }
62
63
64 /**
65 * board_cpu_c_state_set - program CPU into Cx idle state
66 *
67 * RUN Context: could be called from ISR context or task context.
68 *
69 * SMP Consider: STM32L496G-DISCOVERY do not support SMP, so only UP is enough.
70 *
71 * @return PWR_OK or PWR_ERR when failed.
72 */
board_cpu_c_state_set(uint32_t cpuCState,int master)73 static pwr_status_t board_cpu_c_state_set(uint32_t cpuCState, int master)
74 {
75 switch (cpuCState) {
76 case CPU_CSTATE_C0:
77
78 if (master) {
79 /*
80 * do something needed when CPU waked up from C1 or higher
81 * Cx state.
82 */
83 }
84 break;
85
86 case CPU_CSTATE_C1:
87 if ((uint32_t)expeted_sleep_ms < 3) {
88 return PWR_OK;
89 }
90 /* put CPU into C1 state, for ARM we can call WFI instruction
91 to put CPU into C1 state. */
92 PWR_DBG(DBG_DBG, "enter C1\n");
93 sleep_ex_cg((uint32_t)expeted_sleep_ms);
94 PWR_DBG(DBG_DBG, "exit C1\n");
95 break;
96
97 default:
98 PWR_DBG(DBG_ERR, "invalid C state: C%d\n", cpuCState);
99 break;
100 }
101
102 return PWR_OK;
103 }
104
105 /**
106 * board_cpu_pwr_init() is called by HAL lib to
107 * init board powr manage configure.
108 *
109 * RUN Context: could be called from task context only, ISR context is not
110 * supported.
111 *
112 * SMP Consider: STM32L496G-DISCOVERY do not support SMP, so only UP is enough.
113 *
114 * @return PWR_OK or PWR_ERR when failed.
115 */
board_cpu_pwr_init(void)116 pwr_status_t board_cpu_pwr_init(void)
117 {
118 cpu_pwr_t *pCpuNode = NULL;
119 pwr_status_t retVal = PWR_OK;
120 uint32_t cpuIndex = 0; /* 0 for UP */
121
122 pCpuNode = &cpu_pwr_node_core_0;
123 retVal = cpu_pwr_node_init_static("core", 0, pCpuNode);
124 if (retVal != PWR_OK) {
125 return PWR_ERR;
126 }
127
128 /* record this node */
129 retVal = cpu_pwr_node_record(pCpuNode, cpuIndex);
130 if (retVal == PWR_ERR) {
131 return PWR_ERR;
132 }
133
134 /*
135 * According reference manual of STM32L496G-DISCOVERY
136 *
137 * C0 - RUN, Power supplies are on,all clocks are on.
138 * C1 - Sleep mode, CPU clock off, all peripherals including
139 * Cortex®-M4 core peripherals such as NVIC, SysTick, etc. can run
140 * and wake up the CPU when an interrupt or an event occurs.
141 */
142
143 retVal = cpu_pwr_c_method_set(cpuIndex, board_cpu_c_state_set);
144 if (retVal == PWR_ERR) {
145 return PWR_ERR;
146 }
147
148 /* save support C status bitset : C0,C1 */
149 cpu_pwr_c_state_capability_set(cpuIndex, CPU_STATE_BIT(CPU_CSTATE_C0)
150 | CPU_STATE_BIT(CPU_CSTATE_C1)
151 );
152 if (retVal == PWR_ERR) {
153 return PWR_ERR;
154 }
155
156 /*
157 * According reference manual of STM32L496G-DISCOVERY,
158 * the wakeup latency of Cx is:
159 * resume from C1 (Low Power mode) : immediate
160 */
161 cpu_pwr_c_state_latency_save(cpuIndex, CPU_CSTATE_C0, 0);
162 cpu_pwr_c_state_latency_save(cpuIndex, CPU_CSTATE_C1, 0);
163
164 tickless_one_shot_timer_save(CPU_CSTATE_C1, &rtc_one_shot);
165
166 /*
167 Tell the CPU PWR MGMT module which C state is supported with
168 tickless function through tickless_c_states_add(c_state_x).
169 */
170 tickless_c_states_add(CPU_STATE_BIT(CPU_CSTATE_C0)
171 | CPU_STATE_BIT(CPU_CSTATE_C1)
172 );
173
174 #if RHINO_CONFIG_CPU_PWR_SHOW
175 cpu_pwr_info_show();
176 cpu_pwr_state_show();
177 #endif
178
179 return retVal;
180 }
181
pmu_yield_os_check(void)182 uint32_t pmu_yield_os_check(void)
183 {
184 return system_can_yield;
185 }
186
pmu_yield_os_set(int value)187 uint32_t pmu_yield_os_set(int value)
188 {
189 system_can_yield = value;
190 }
191
pmu_set_sysactive_time(uint32_t timeout)192 uint32_t pmu_set_sysactive_time(uint32_t timeout)
193 {
194 pwrmgmt_cpu_active_msec_set(timeout);
195 }
196
pmu_release_wakelock(uint32_t nDeviceId)197 void pmu_release_wakelock(uint32_t nDeviceId)
198 {
199 nDeviceId = nDeviceId + 17;
200 pwrmgmt_cpu_lowpower_resume(nDeviceId);
201 }
202
pmu_acquire_wakelock(uint32_t nDeviceId)203 void pmu_acquire_wakelock(uint32_t nDeviceId)
204 {
205 nDeviceId = nDeviceId + 17;
206 pwrmgmt_cpu_lowpower_suspend(nDeviceId);
207 }
208
pmu_exec_wakeup_hook_funs(u32 nDeviceIdMax)209 void pmu_exec_wakeup_hook_funs(u32 nDeviceIdMax)
210 {
211 PSM_DD_HOOK_INFO *pPsmDdHookInfo = NULL;
212 u32 nDeviceIdOffset = 0;
213
214 for( nDeviceIdOffset = 0; nDeviceIdOffset < nDeviceIdMax; nDeviceIdOffset++) {
215 pPsmDdHookInfo = &gPsmDdHookInfo[nDeviceIdOffset];
216
217 /*if this device register and sleep_hook_fun not NULL*/
218 if(pPsmDdHookInfo && pPsmDdHookInfo->wakeup_hook_fun) {
219 pPsmDdHookInfo->wakeup_hook_fun(0, pPsmDdHookInfo->wakeup_param_ptr);
220 }
221 }
222 }
223
pmu_exec_sleep_hook_funs(void)224 u32 pmu_exec_sleep_hook_funs(void)
225 {
226 PSM_DD_HOOK_INFO *pPsmDdHookInfo = NULL;
227 u32 nDeviceIdOffset = 0;
228 u32 ret = TRUE;
229
230 for( nDeviceIdOffset = 0; nDeviceIdOffset < PMU_MAX; nDeviceIdOffset++) {
231 pPsmDdHookInfo = &gPsmDdHookInfo[nDeviceIdOffset];
232
233 /*if this device register and sleep_hook_fun not NULL*/
234 if(pPsmDdHookInfo && pPsmDdHookInfo->sleep_hook_fun) {
235 ret = pPsmDdHookInfo->sleep_hook_fun(0, pPsmDdHookInfo->sleep_param_ptr);
236
237 if (ret == FALSE) {
238 break;
239 }
240 }
241 }
242
243 return nDeviceIdOffset;
244 }
245
pmu_register_sleep_callback(u32 nDeviceId,PSM_HOOK_FUN sleep_hook_fun,void * sleep_param_ptr,PSM_HOOK_FUN wakeup_hook_fun,void * wakeup_param_ptr)246 void pmu_register_sleep_callback(u32 nDeviceId, PSM_HOOK_FUN sleep_hook_fun, void* sleep_param_ptr, PSM_HOOK_FUN wakeup_hook_fun, void* wakeup_param_ptr)
247 {
248 PSM_DD_HOOK_INFO *pPsmDdHookInfo = NULL;
249
250 assert_param(nDeviceId < PMU_MAX);
251 assert_param((sleep_hook_fun != NULL) || (wakeup_hook_fun != NULL));
252
253 pPsmDdHookInfo = &gPsmDdHookInfo[nDeviceId];
254 pPsmDdHookInfo->nDeviceId = nDeviceId;
255 pPsmDdHookInfo->sleep_hook_fun = sleep_hook_fun;
256 pPsmDdHookInfo->sleep_param_ptr = sleep_param_ptr;
257 pPsmDdHookInfo->wakeup_hook_fun = wakeup_hook_fun;
258 pPsmDdHookInfo->wakeup_param_ptr = wakeup_param_ptr;
259 }
260
pmu_unregister_sleep_callback(u32 nDeviceId)261 void pmu_unregister_sleep_callback(u32 nDeviceId)
262 {
263 PSM_DD_HOOK_INFO *pPsmDdHookInfo = NULL;
264
265 assert_param(nDeviceId < PMU_MAX);
266
267 pPsmDdHookInfo = &gPsmDdHookInfo[nDeviceId];
268
269 _memset(pPsmDdHookInfo, 0x00, sizeof(PSM_DD_HOOK_INFO));
270 }
271
pmu_acquire_deepwakelock(uint32_t nDeviceId)272 void pmu_acquire_deepwakelock(uint32_t nDeviceId)
273 {
274 deepwakelock |= BIT(nDeviceId);
275 }
276
pmu_release_deepwakelock(uint32_t nDeviceId)277 void pmu_release_deepwakelock(uint32_t nDeviceId)
278 {
279 deepwakelock &= ~BIT(nDeviceId);
280 }
281
282 #else
pmu_yield_os_check(void)283 uint32_t pmu_yield_os_check(void)
284 {
285 return 1;
286 }
pmu_release_wakelock(uint32_t nDeviceId)287 void pmu_release_wakelock(uint32_t nDeviceId)
288 {
289
290 }
pmu_acquire_wakelock(uint32_t nDeviceId)291 void pmu_acquire_wakelock(uint32_t nDeviceId)
292 {
293
294 }
pmu_set_sysactive_time(uint32_t timeout)295 uint32_t pmu_set_sysactive_time(uint32_t timeout)
296 {
297 return 0;
298 }
299
pmu_register_sleep_callback(u32 nDeviceId,PSM_HOOK_FUN sleep_hook_fun,void * sleep_param_ptr,PSM_HOOK_FUN wakeup_hook_fun,void * wakeup_param_ptr)300 void pmu_register_sleep_callback(u32 nDeviceId, PSM_HOOK_FUN sleep_hook_fun, void* sleep_param_ptr, PSM_HOOK_FUN wakeup_hook_fun, void* wakeup_param_ptr)
301 {
302
303 }
304
pmu_unregister_sleep_callback(u32 nDeviceId)305 void pmu_unregister_sleep_callback(u32 nDeviceId){
306
307 }
308 #endif /* AOS_COMP_PWRMGMT */
309
310