1 /*
2 * Copyright (c) 2020 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <soc.h>
11 #include <zephyr/pm/pm.h>
12 #include <zephyr/logging/log.h>
13 #define LOG_LEVEL LOG_LEVEL_DBG
14 LOG_MODULE_REGISTER(pwrmgmt_test);
15
16 #define SLP_STATES_SUPPORTED 2ul
17
18 /* Thread properties */
19 #define TASK_STACK_SIZE 1024ul
20 #define PRIORITY K_PRIO_COOP(5)
21 /* Sleep time should be lower than SUSPEND_TO_IDLE residency time */
22 #define THREAD_A_SLEEP_TIME 100ul
23 #define THREAD_B_SLEEP_TIME 1000ul
24
25 /* Maximum latency should be less than 300 ms */
26 #define MAX_EXPECTED_MS_LATENCY 500ul
27
28 /* Sleep some extra time than minimum residency */
29 #define DP_EXTRA_SLP_TIME 1100ul
30 #define LT_EXTRA_SLP_TIME 500ul
31
32 #define SEC_TO_MSEC 1000ul
33
34 K_THREAD_STACK_DEFINE(stack_a, TASK_STACK_SIZE);
35 K_THREAD_STACK_DEFINE(stack_b, TASK_STACK_SIZE);
36
37 static struct k_thread thread_a_id;
38 static struct k_thread thread_b_id;
39
40 struct pm_counter {
41 uint8_t entry_cnt;
42 uint8_t exit_cnt;
43 };
44
45 /* Track time elapsed */
46 static int64_t trigger_time;
47 static bool checks_enabled;
48 /* Track entry/exit to sleep */
49 struct pm_counter pm_counters[SLP_STATES_SUPPORTED];
50
51 static const struct pm_state_info residency_info[] =
52 PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
53 static size_t residency_info_len = DT_NUM_CPU_POWER_STATES(DT_NODELABEL(cpu0));
54
55
56 /* Instrumentation to measure latency and track entry exit via gpios
57 *
58 * In EVB set following jumpers:
59 * JP99 7-8 closed
60 * JP99 10-11 closed
61 * JP75 29-30 closed
62 * JP75 32-33 closed
63 *
64 * In EVB probe following pins:
65 * JP25.3 (GPIO012_LT) light sleep
66 * JP25.5 (GPIO013_DP) deep sleep
67 * JP25.7 (GPIO014_TRIG) trigger in app
68 * JP75.29 (GPIO60_CLK_OUT)
69 */
70
pm_latency_check(void)71 static void pm_latency_check(void)
72 {
73 int64_t latency;
74 int secs;
75 int msecs;
76
77 latency = k_uptime_delta(&trigger_time);
78 secs = (int)(latency / SEC_TO_MSEC);
79 msecs = (int)(latency % SEC_TO_MSEC);
80 LOG_INF("PM sleep entry latency %d.%03d seconds", secs, msecs);
81
82 if (secs > 0) {
83 LOG_WRN("Sleep entry latency is too high");
84 return;
85 }
86
87 if (msecs > MAX_EXPECTED_MS_LATENCY) {
88 LOG_WRN("Sleep entry latency is higher than expected");
89 }
90 }
91
92 /* Hooks to count entry/exit */
notify_pm_state_entry(enum pm_state state)93 static void notify_pm_state_entry(enum pm_state state)
94 {
95 if (!checks_enabled) {
96 return;
97 }
98
99 switch (state) {
100 case PM_STATE_SUSPEND_TO_IDLE:
101 GPIO_CTRL_REGS->CTRL_0012 = 0x240ul;
102 pm_counters[0].entry_cnt++;
103 break;
104 case PM_STATE_SUSPEND_TO_RAM:
105 GPIO_CTRL_REGS->CTRL_0013 = 0x240ul;
106 pm_counters[1].entry_cnt++;
107 pm_latency_check();
108 break;
109 default:
110 break;
111 }
112 }
113
notify_pm_state_exit(enum pm_state state)114 static void notify_pm_state_exit(enum pm_state state)
115 {
116 if (!checks_enabled) {
117 return;
118 }
119
120 switch (state) {
121 case PM_STATE_SUSPEND_TO_IDLE:
122 GPIO_CTRL_REGS->CTRL_0012 = 0x10240ul;
123 pm_counters[0].exit_cnt++;
124 break;
125 case PM_STATE_SUSPEND_TO_RAM:
126 GPIO_CTRL_REGS->CTRL_0013 = 0x10240ul;
127 pm_counters[1].exit_cnt++;
128 break;
129 default:
130 break;
131 }
132 }
133
pm_check_counters(uint8_t cycles)134 static void pm_check_counters(uint8_t cycles)
135 {
136 for (int i = 0; i < SLP_STATES_SUPPORTED; i++) {
137 LOG_INF("PM state[%d] entry counter %d", i,
138 pm_counters[i].entry_cnt);
139 LOG_INF("PM state[%d] exit counter %d", i,
140 pm_counters[i].exit_cnt);
141
142 if (pm_counters[i].entry_cnt != pm_counters[i].exit_cnt) {
143 LOG_WRN("PM counters entry/exit mismatch");
144 }
145
146 if (pm_counters[i].entry_cnt != cycles) {
147 LOG_WRN("PM counter mismatch expected: %d", cycles);
148 }
149
150 pm_counters[i].entry_cnt = 0;
151 pm_counters[i].exit_cnt = 0;
152 }
153 }
154
pm_reset_counters(void)155 static void pm_reset_counters(void)
156 {
157 for (int i = 0; i < SLP_STATES_SUPPORTED; i++) {
158 pm_counters[i].entry_cnt = 0;
159 pm_counters[i].exit_cnt = 0;
160 }
161
162 checks_enabled = false;
163 GPIO_CTRL_REGS->CTRL_0014 = 0x10240UL;
164 }
165
pm_trigger_marker(void)166 static void pm_trigger_marker(void)
167 {
168 trigger_time = k_uptime_get();
169
170 /* Directly access a pin to mark sleep trigger */
171 GPIO_CTRL_REGS->CTRL_0014 = 0x00240UL;
172 printk("PM >\n");
173 }
174
pm_exit_marker(void)175 static void pm_exit_marker(void)
176 {
177 int64_t residency_delta;
178 int secs;
179 int msecs;
180
181 /* Directly access a pin */
182 GPIO_CTRL_REGS->CTRL_0014 = 0x10240UL;
183 printk("PM <\n");
184
185 if (trigger_time > 0) {
186 residency_delta = k_uptime_delta(&trigger_time);
187 secs = (int)(residency_delta / SEC_TO_MSEC);
188 msecs = (int)(residency_delta % SEC_TO_MSEC);
189 LOG_INF("PM sleep residency %d.%03d seconds", secs, msecs);
190 }
191 }
192
task_a_init(void)193 static int task_a_init(void)
194 {
195 LOG_INF("Thread task A init");
196
197 return 0;
198 }
199
task_b_init(void)200 static int task_b_init(void)
201 {
202 printk("Thread task B init");
203
204 return 0;
205 }
206
task_a_thread(void * p1,void * p2,void * p3)207 void task_a_thread(void *p1, void *p2, void *p3)
208 {
209 while (true) {
210 k_msleep(THREAD_A_SLEEP_TIME);
211 printk("A");
212 }
213 }
214
task_b_thread(void * p1,void * p2,void * p3)215 static void task_b_thread(void *p1, void *p2, void *p3)
216 {
217 while (true) {
218 k_msleep(THREAD_B_SLEEP_TIME);
219 printk("B");
220 }
221 }
222
create_tasks(void)223 static void create_tasks(void)
224 {
225 task_a_init();
226 task_b_init();
227
228 k_thread_create(&thread_a_id, stack_a, TASK_STACK_SIZE, task_a_thread,
229 NULL, NULL, NULL, PRIORITY, K_INHERIT_PERMS, K_FOREVER);
230 k_thread_create(&thread_b_id, stack_b, TASK_STACK_SIZE, task_b_thread,
231 NULL, NULL, NULL, PRIORITY, K_INHERIT_PERMS, K_FOREVER);
232
233 k_thread_start(&thread_a_id);
234 k_thread_start(&thread_b_id);
235
236 }
237
destroy_tasks(void)238 static void destroy_tasks(void)
239 {
240 k_thread_abort(&thread_a_id);
241 k_thread_abort(&thread_b_id);
242
243 k_thread_join(&thread_a_id, K_FOREVER);
244 k_thread_join(&thread_b_id, K_FOREVER);
245 }
246
suspend_all_tasks(void)247 static void suspend_all_tasks(void)
248 {
249 k_thread_suspend(&thread_a_id);
250 k_thread_suspend(&thread_b_id);
251 }
252
resume_all_tasks(void)253 static void resume_all_tasks(void)
254 {
255 k_thread_resume(&thread_a_id);
256 k_thread_resume(&thread_b_id);
257 }
258
259 static struct pm_notifier notifier = {
260 .state_entry = notify_pm_state_entry,
261 .state_exit = notify_pm_state_exit,
262 };
263
test_pwr_mgmt_multithread(bool use_logging,uint8_t cycles)264 int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles)
265 {
266 uint8_t iterations = cycles;
267 /* Ensure we can enter deep sleep when stopping threads
268 * No UART output should occur when threads are suspended
269 * Test to verify Zephyr RTOS issue #20033
270 * https://github.com/zephyrproject-rtos/zephyr/issues/20033
271 */
272
273 pm_notifier_register(¬ifier);
274 create_tasks();
275
276 LOG_WRN("PM multi-thread test started for cycles: %d, logging: %d",
277 cycles, use_logging);
278
279 checks_enabled = true;
280 while (iterations-- > 0) {
281
282 /* Light sleep cycle */
283 LOG_INF("Suspend...");
284 suspend_all_tasks();
285 LOG_INF("About to enter light sleep");
286 k_msleep((residency_info[0].min_residency_us / 1000U) +
287 LT_EXTRA_SLP_TIME);
288 k_busy_wait(100);
289
290 if (use_logging) {
291 LOG_INF("Wake from Light Sleep");
292 } else {
293 printk("Wake from Light Sleep\n");
294 }
295
296 LOG_INF("Resume");
297 resume_all_tasks();
298
299 /* Deep sleep cycle */
300 LOG_INF("Suspend...");
301 suspend_all_tasks();
302 LOG_INF("About to enter deep sleep");
303
304 /* GPIO toggle to measure latency for deep sleep */
305 pm_trigger_marker();
306 k_msleep(
307 (residency_info[residency_info_len - 1].min_residency_us /
308 1000U) + DP_EXTRA_SLP_TIME);
309 k_busy_wait(100);
310
311 if (use_logging) {
312 LOG_INF("Wake from Deep Sleep");
313 } else {
314 printk("Wake from Deep Sleep\n");
315 }
316
317 pm_exit_marker();
318 LOG_INF("Resume");
319 resume_all_tasks();
320 }
321
322 destroy_tasks();
323
324 LOG_INF("PM multi-thread completed");
325 pm_check_counters(cycles);
326 pm_reset_counters();
327 pm_notifier_unregister(¬ifier);
328
329 return 0;
330 }
331
test_pwr_mgmt_singlethread(bool use_logging,uint8_t cycles)332 int test_pwr_mgmt_singlethread(bool use_logging, uint8_t cycles)
333 {
334 uint8_t iterations = cycles;
335
336 LOG_WRN("PM single-thread test started for cycles: %d, logging: %d",
337 cycles, use_logging);
338
339 pm_notifier_register(¬ifier);
340 checks_enabled = true;
341 while (iterations-- > 0) {
342
343 /* Trigger Light Sleep 1 state. 48MHz PLL stays on */
344 LOG_INF("About to enter light sleep");
345 k_msleep((residency_info[0].min_residency_us / 1000U) +
346 LT_EXTRA_SLP_TIME);
347 k_busy_wait(100);
348
349 if (use_logging) {
350 LOG_INF("Wake from Light Sleep");
351 } else {
352 printk("Wake from Light Sleep\n");
353 }
354
355 /* Trigger Deep Sleep 1 state. 48MHz PLL off */
356 LOG_INF("About to enter deep Sleep");
357
358 /* GPIO toggle to measure latency */
359 pm_trigger_marker();
360 k_msleep(
361 (residency_info[residency_info_len - 1].min_residency_us /
362 1000U) + DP_EXTRA_SLP_TIME);
363 k_busy_wait(100);
364
365 if (use_logging) {
366 LOG_INF("Wake from Deep Sleep");
367 } else {
368 printk("Wake from Deep Sleep\n");
369 }
370
371 pm_exit_marker();
372 }
373
374 LOG_INF("PM single-thread completed");
375 pm_check_counters(cycles);
376 pm_reset_counters();
377 pm_notifier_unregister(¬ifier);
378
379 return 0;
380 }
381