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