1 /*
2 * Copyright (c) 2020 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/pm/pm.h>
10 #include <zephyr/logging/log.h>
11 #define LOG_LEVEL LOG_LEVEL_DBG
12 LOG_MODULE_REGISTER(pwrmgmt_test);
13
14 /* Thread properties */
15 #undef TASK_STACK_SIZE
16 #define TASK_STACK_SIZE 1024ul
17 #define PRIORITY K_PRIO_COOP(5)
18
19 /* Sleep time should be lower than SUSPEND_TO_IDLE */
20 #define THREAD_A_SLEEP_TIME 100ul
21 #define THREAD_B_SLEEP_TIME 1000ul
22
23 /* Maximum latency should be less than 500 ms */
24 #define MAX_EXPECTED_MS_LATENCY 500ul
25
26 /* Sleep some extra time than minimum residency:
27 * - for light it should be very little so that we get only into light sleep
28 * and not accidentally into a deep sleep.
29 * - for deep sleep it can be very long as we want to ensure that we enter
30 * the deepest sleep state possible.
31 */
32 #define LT_EXTRA_SLP_TIME_US 50ul
33 #define DP_EXTRA_SLP_TIME_US 1100000ul
34
35 #define SEC_TO_MSEC 1000ul
36
37 K_THREAD_STACK_DEFINE(stack_a, TASK_STACK_SIZE);
38 K_THREAD_STACK_DEFINE(stack_b, TASK_STACK_SIZE);
39
40 static struct k_thread thread_a_id;
41 static struct k_thread thread_b_id;
42
43 struct pm_counter {
44 uint8_t entry_cnt;
45 uint8_t exit_cnt;
46 };
47
48 /* Track time elapsed */
49 static int64_t trigger_time;
50 static bool checks_enabled;
51 static bool measure_entry_latency;
52 /* Track entry/exit to sleep */
53 struct pm_counter pm_counters[PM_STATE_COUNT];
54
55 static const struct pm_state_info residency_info[] =
56 PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
57 static size_t residency_info_len = DT_NUM_CPU_POWER_STATES(DT_NODELABEL(cpu0));
58
pm_latency_check(void)59 static void pm_latency_check(void)
60 {
61 int64_t latency;
62 int secs;
63 int msecs;
64
65 latency = k_uptime_get() - trigger_time;
66 secs = (int)(latency / SEC_TO_MSEC);
67 msecs = (int)(latency % SEC_TO_MSEC);
68
69 zassert_false(secs > 0 || msecs > MAX_EXPECTED_MS_LATENCY,
70 "Sleep entry latency is too high: %d.%03d s.",
71 secs, msecs);
72 }
73
notify_pm_state_entry(enum pm_state state)74 static void notify_pm_state_entry(enum pm_state state)
75 {
76 if (!checks_enabled) {
77 return;
78 }
79
80 pm_counters[(int)state].entry_cnt++;
81
82 if (measure_entry_latency) {
83 pm_latency_check();
84 /* After first PM entry disable further checks since there can
85 * be multiple PM entry-exit events e.g. due to spurious
86 * wakeup
87 */
88 measure_entry_latency = false;
89 }
90 }
91
notify_pm_state_exit(enum pm_state state)92 static void notify_pm_state_exit(enum pm_state state)
93 {
94 if (!checks_enabled) {
95 return;
96 }
97
98 pm_counters[(int)state].exit_cnt++;
99 }
100
101 static struct pm_notifier notifier = {
102 .state_entry = notify_pm_state_entry,
103 .state_exit = notify_pm_state_exit,
104 };
105
pm_check_counters(uint8_t cycles)106 static void pm_check_counters(uint8_t cycles)
107 {
108 for (int i = 0; i < PM_STATE_COUNT; i++) {
109
110 LOG_INF("PM state[%d] entry counter %d\n", i,
111 pm_counters[i].entry_cnt);
112 LOG_INF("PM state[%d] exit counter %d\n", i,
113 pm_counters[i].exit_cnt);
114
115 zassert_equal(pm_counters[i].entry_cnt,
116 pm_counters[i].exit_cnt,
117 "PM counters entry/exit mismatch");
118
119 pm_counters[i].entry_cnt = 0;
120 pm_counters[i].exit_cnt = 0;
121 }
122 }
123
pm_reset_counters(void)124 static void pm_reset_counters(void)
125 {
126 for (int i = 0; i < PM_STATE_COUNT; i++) {
127 pm_counters[i].entry_cnt = 0;
128 pm_counters[i].exit_cnt = 0;
129 }
130
131 checks_enabled = false;
132 measure_entry_latency = false;
133 }
134
pm_trigger_marker(void)135 static void pm_trigger_marker(void)
136 {
137 trigger_time = k_uptime_get();
138
139 printk("PM >\n");
140 }
141
pm_exit_marker(void)142 static void pm_exit_marker(void)
143 {
144 int64_t residency_delta;
145 int secs;
146 int msecs;
147
148 printk("PM <\n");
149
150 if (trigger_time > 0) {
151 residency_delta = k_uptime_get() - trigger_time;
152 secs = (int)(residency_delta / SEC_TO_MSEC);
153 msecs = (int)(residency_delta % SEC_TO_MSEC);
154 LOG_INF("PM sleep residency %d.%03d seconds", secs, msecs);
155 }
156 }
157
task_a_init(void)158 static int task_a_init(void)
159 {
160 LOG_INF("Thread task A init");
161
162 return 0;
163 }
164
task_b_init(void)165 static int task_b_init(void)
166 {
167 LOG_INF("Thread task B init");
168
169 return 0;
170 }
171
task_a_thread(void * p1,void * p2,void * p3)172 void task_a_thread(void *p1, void *p2, void *p3)
173 {
174 while (true) {
175 k_msleep(THREAD_A_SLEEP_TIME);
176 printk("A");
177 }
178 }
179
task_b_thread(void * p1,void * p2,void * p3)180 static void task_b_thread(void *p1, void *p2, void *p3)
181 {
182 while (true) {
183 k_msleep(THREAD_B_SLEEP_TIME);
184 printk("B");
185 }
186 }
187
create_tasks(void)188 static void create_tasks(void)
189 {
190 task_a_init();
191 task_b_init();
192
193 k_thread_create(&thread_a_id, stack_a, TASK_STACK_SIZE, task_a_thread,
194 NULL, NULL, NULL, PRIORITY, K_INHERIT_PERMS, K_FOREVER);
195 k_thread_create(&thread_b_id, stack_b, TASK_STACK_SIZE, task_b_thread,
196 NULL, NULL, NULL, PRIORITY, K_INHERIT_PERMS, K_FOREVER);
197
198 k_thread_start(&thread_a_id);
199 k_thread_start(&thread_b_id);
200
201 }
202
destroy_tasks(void)203 static void destroy_tasks(void)
204 {
205 k_thread_abort(&thread_a_id);
206 k_thread_abort(&thread_b_id);
207
208 k_thread_join(&thread_a_id, K_FOREVER);
209 k_thread_join(&thread_b_id, K_FOREVER);
210 }
211
suspend_all_tasks(void)212 static void suspend_all_tasks(void)
213 {
214 k_thread_suspend(&thread_a_id);
215 k_thread_suspend(&thread_b_id);
216 }
217
resume_all_tasks(void)218 static void resume_all_tasks(void)
219 {
220 k_thread_resume(&thread_a_id);
221 k_thread_resume(&thread_b_id);
222 }
223
test_pwr_mgmt_multithread(uint8_t cycles)224 int test_pwr_mgmt_multithread(uint8_t cycles)
225 {
226 uint8_t iterations = cycles;
227
228 pm_notifier_register(¬ifier);
229 create_tasks();
230
231 LOG_INF("PM multi-thread test started for cycles: %d", cycles);
232
233 checks_enabled = true;
234 while (iterations-- > 0) {
235
236 /* Light sleep cycle */
237 LOG_INF("Suspend...");
238 suspend_all_tasks();
239 LOG_INF("About to enter light sleep");
240 measure_entry_latency = true;
241 pm_trigger_marker();
242 k_usleep(residency_info[0].min_residency_us +
243 LT_EXTRA_SLP_TIME_US);
244
245 LOG_INF("Wake from Light Sleep");
246 pm_exit_marker();
247 LOG_INF("Resume");
248 resume_all_tasks();
249
250 /* Deep sleep cycle */
251 /* Platforms that do not automatically enter deep sleep */
252 /* states in its residency policy will simply enter light */
253 /* sleep states instead */
254 LOG_INF("Suspend...");
255 suspend_all_tasks();
256 LOG_INF("About to enter deep sleep");
257
258 measure_entry_latency = true;
259 pm_trigger_marker();
260 k_usleep(
261 residency_info[residency_info_len - 1].min_residency_us +
262 DP_EXTRA_SLP_TIME_US);
263
264 LOG_INF("Wake from Deep Sleep");
265 pm_exit_marker();
266 LOG_INF("Resume");
267 resume_all_tasks();
268 }
269
270 destroy_tasks();
271 pm_notifier_unregister(¬ifier);
272
273 LOG_INF("PM multi-thread completed");
274 pm_check_counters(cycles);
275 pm_reset_counters();
276
277 return 0;
278 }
279
test_pwr_mgmt_singlethread(uint8_t cycles)280 int test_pwr_mgmt_singlethread(uint8_t cycles)
281 {
282 uint8_t iterations = cycles;
283
284 LOG_INF("PM single-thread test started for cycles: %d", cycles);
285
286 pm_notifier_register(¬ifier);
287 checks_enabled = true;
288 while (iterations-- > 0) {
289
290 /* Trigger Light Sleep 1 state. 48MHz PLL stays on */
291 LOG_INF("About to enter light sleep");
292 measure_entry_latency = true;
293 pm_trigger_marker();
294 k_usleep(residency_info[0].min_residency_us +
295 LT_EXTRA_SLP_TIME_US);
296 LOG_INF("Wake from Light Sleep");
297 pm_exit_marker();
298
299 /* Trigger Deep Sleep 1 state. 48MHz PLL off */
300 /* Platforms that do not automatically enter deep sleep */
301 /* states in its residency policy will simply enter light */
302 /* sleep states instead */
303 LOG_INF("About to enter deep Sleep");
304 measure_entry_latency = true;
305 pm_trigger_marker();
306 k_usleep(
307 residency_info[residency_info_len - 1].min_residency_us +
308 DP_EXTRA_SLP_TIME_US);
309 LOG_INF("Wake from Deep Sleep");
310 pm_exit_marker();
311 }
312
313 pm_notifier_unregister(¬ifier);
314 LOG_INF("PM single-thread completed");
315 pm_check_counters(cycles);
316 pm_reset_counters();
317
318 return 0;
319 }
320
test_dummy_init(void)321 int test_dummy_init(void)
322 {
323 uint8_t iterations = 1;
324
325 LOG_INF("PM dummy single-thread test started for one cycle");
326
327 checks_enabled = true;
328 while (iterations-- > 0) {
329 LOG_INF("About to enter light sleep");
330 measure_entry_latency = true;
331 pm_trigger_marker();
332 k_usleep(residency_info[0].min_residency_us +
333 LT_EXTRA_SLP_TIME_US);
334 LOG_INF("Wake from Light Sleep");
335 pm_exit_marker();
336
337 LOG_INF("About to enter deep Sleep");
338 measure_entry_latency = true;
339 pm_trigger_marker();
340 k_usleep(
341 residency_info[residency_info_len - 1].min_residency_us +
342 DP_EXTRA_SLP_TIME_US);
343 LOG_INF("Wake from Deep Sleep");
344 pm_exit_marker();
345 }
346
347 LOG_INF("PM dummy single-thread completed");
348 pm_reset_counters();
349 return 0;
350 }
351